今天,笔者在编写跳跃脚本时可谓是困难重重,包括但不限于跳都跳不起来,间歇性无法识别空格,化身土行孙直接穿透地形......为了从根源上解决问题,弄清逻辑,就得从Unity2D物理系统的重要组成部分——刚体组件( Rigidbody 2D )讲起。
Rigidbody 2D
在好好了解刚体组件后才深知,有时Unity的中文翻译不可全信,他可能会让你理解错一些参数的意思。
在Dynamic类型刚体中有个重要参数——重力大小。但其原文为Gravity Scale,意为重力缩放。表示该刚体受重力影响的倍数,而真正的重力大小是在Unity物理系统全局中定义的,默认为(0, 9.81)的向量,可以在项目设置中更改。
在这个地方,“重力”其实对应现实世界中重力加速度的概念。而其他的“力”,则遵循F=ma这样的牛顿第一定律。比如刚体组件中的方法AddForce(Vector2 force, ForceMode2D mode)能够为物体施加一个指定方向的力( 没错力也是Vector2类型 ),如果我在脚本中编写一段代码为物体添加一个与重力等大反向的力,那么当我托起人物时,人物会悬浮在空中。这说明了该物体所受真正重力的大小其实为【重力加速度 * 质量 * 重力缩放】。
//让这个同重力一样持续作用在物体上
void FixedUpdate()
{
//与重力等大反向,F=ma,这里10是重力加速度的大小
Vector2 force = new Vector2(0, 10 * body.mass * body.gravityScale);
//两种作用模式:
//1、ForceMode2D.Force 施加持续均匀的力
//2、ForceMode2D.Impulse 施加一个瞬时的力
body.AddForce(force, ForceMode2D.Force);
}
总的来说,Unity物理系统的力依然遵循现实世界中F=ma这样的规律,而项目设置中所谓的“重力”,指的是重力加速度的值。其中,刚体的质量会在受力时影响物体的加速度,也会影响物体的惯性与碰撞效果。在实际开发中可以根据计算设定合适的力或加速度大小。
现在再来看看刚体组件的其他参数:
-
身体类型
-
动态( Dynamic ):完全受Unity物理引擎控制,会发受到碰撞、力等影响。默认情况下就是这个。
-
运动学( Kinematic ):不受物理引擎影响,用户自己编写脚本控制物体。
-
静态( Static ):可以碰撞,但不受到任何力的影响,适用于静止物体。
-
-
材质:要注意这里一定只能是“物理材质2D”( Physics Material 2D ),既不是“材质”也不是“物理材质”。这里可以设定物体表面的摩擦系数与弹性系数。
-
模拟的:刚体是否参与物理模拟。当你调试其他刚体不想让此物体造成影响时,可以关掉。
-
使用自动质量:意为自动检测质量,计算公式为“碰撞体面积*密度”,这里的密度应该是Unity内部默认的,但可以通过脚本修改。这属于碰撞体组件的内容了。
-
线性阻尼:影响位置移动系数
-
角阻尼:影响旋转移动系数
-
碰撞检测:分为离散型( Discrete )和持续型( Continuous ),离散型在每一帧进行检测但只检测物体当前所在位置。而持续型通过插值或射线检测等手段,在移动路径上进行连续检测。离散型会造成我在文章开头所说的“土行孙”现象,如果移动过快,可能导致物体穿过地面,持续检测则避免了这样的错误( 也要考虑CPU的负担 )。
-
休眠模式:在物体休眠时,可以节省处理器计算空间,类似于电脑的待机状态。休眠状态的物体可以通过碰撞等外力影响而被唤醒,继续参与物理模拟。其中“开始唤醒”是指物静止一段时间后会进入休眠状态。
-
插值( Interpolation ),也是个重要的概念。
- 在 Unity 的物理引擎中,刚体的运动是基于物理更新的“FixedUpdate”,而渲染是基于帧更新的“Update”。由于物理更新和帧更新的频率可能不同,刚体的运动可能会出现卡顿或不平滑的现象。插值通过在物理更新和帧更新之间进行平滑过渡,使物体的运动看起来更加流畅。
- 插值( Interpolate ):基于上一帧物理状态插值。
- 外推( Extrapolate ):基于下一帧预测的物理状态插值,开销较高。
-
约束( Constraints ):约束位置。
-
图层覆盖( Layer Overrides ):设置要和哪些图层进行碰撞等物理模拟。在进行几次试验后,笔者猜想:可能“排除层”的优先级要高于“包含层”,排除的图层就算在包含层中被选中了也无法碰撞。当排除层选择Nothing时,就算包含层也为Nothing也依旧发生碰撞。
-
但事实是猜对了,但没完全猜对。因为我忽略了我们熟知的图形碰撞矩阵。
-
他们的优先级应该是这样的:排除层>包含层>图形碰撞矩阵。所以当包含层、排除层都选为Nothing时,其实真正含义是“我们都不限制图形碰撞矩阵发挥了,图形碰撞矩阵说谁碰撞谁就碰撞”。但只要排除层有排除的图层,就算矩阵里可以碰撞也没用,只要包含层里有包含的图层,就算矩阵不能碰撞也得碰。
事到如今,这个Rigidbody 2D终于是完全搞懂了,如有补充纠正,欢迎留言。