5_物理模块
物理模块相关的知识点

物理和时间
物理模块的更新是在FixedUpdate()中进行的,默认为0.02秒更新一次(每秒50次),这个时间被称为步长
Unity的更新循环是以每帧更新的方式进行的,在每一帧中,首先执行物理模块的更新,然后再进行渲染和其它逻辑的处理,在更新时会检查是否需要执行FixedUpdate()
如果自上次固定更新以来经过的时间太短(小于0.02秒),则跳过当前的固定更新

最大允许的时间步长
如果自上次固定更新以来经过了很长时间,那么固定更新将持续计算,直到赶上当前时间
比如上一帧花了100毫秒进行渲染,那么物理引擎需要执行5次FixedUpdate(),以尽量保证物理模块的稳定,但由于这期间渲染模块一直没有更新,等到FixedUpdate()执行完后更新渲染画面,物体就会有瞬移了一段距离的感觉
另一种情况是执行一次FixedUpdate()花的时间超过了一个固定时长(0.02秒),这样会导致物理模块永远无法摆脱固定的更新循环,为了防止这样的情况发生,Unity会设置最长执行时间,来限制在FixedUpdate()阶段所花的时间,如果处理时间太长,他将停止并放弃进一步的处理,直到渲染和其它逻辑走完后再进行FixedUpdate()的调用
运行时修改物理组件
Rigidbody组件中的方法更新频率与物理模块更新频率紧密耦合,因此如果要修改Rigidbody中的参数最好在FixedUpdate中进行修改,如果在Update中修改可能会导致在不同设备上运行的结果不一致
静态碰撞器和动态碰撞器
动态碰撞意味着这个这个物体包含了Rigidbody和碰撞器组件,此时这个物体会对其它物体的碰撞做出反应
静态碰撞则是没有附加Rigidbody,只包含了碰撞器组件的物体,这样的物体会与其它物体发生碰撞,但本身不会有任何反应
碰撞检测
Rigidbody有4种类型的碰撞检测
Discrete(离散):在每次FixedUpdate调用时检测是否碰撞
Continuous(连续):对物体运行的轨迹进行插值,以检测在FixedUpdate方法调用之间产生的碰撞
ContinuousDynamic(连续动态):与Continuous相同,但主要用于对多个动态物体的连续碰撞检测
ContinuousSpeculative(连续推测):通过物体在离散时的运动间隔内推算可能发生的碰撞
性能开销:Discrete < ContinuousSpeculative < Continuous < ContinuousDynamic
检测准确性:ContinuousDynamic < ContinuousSpeculative< Continuous< Discrete
碰撞矩阵
物理引擎具有一个碰撞矩阵,这个矩阵定义允许哪些对象之间会发生碰撞
可以通过Editor → Project Setting → Physics/Physics2D → Layer CollisionMatrix 访问

Rigidbody激活和休眠状态
当Rigidbody处于休眠状态时,在FixedUpdate更新过程中,物理模块就不会对其进行处理,直到它被外力或碰撞事件唤醒
判断一个物体是否会处于休眠状态是根据物体的移动速度和角速度确定
可以通过Editor → Project Setting → Physics → SleepThreshold 修改
SleepThreshold指的是当移动速度和角速度,都低于该值时就进入休眠模式

使用物理模块的一些注意点
缩放:尽可能的使游戏世界中所有物理物体的缩放接近(1,1,1),物体大小应该反应真实世界的物体尺度,因为缩放过大会导致物体的移动速度比预期慢得多,如果物体放大了5倍那么重力也会减弱5倍
位置:保证所有对象尽量接近世界原点(0,0,0),将具有更好的浮点数精度,提高模拟的一致性
质量:质量以浮点数的形式储存在刚体组件的质量属性下,不同物体之间的质量应该控制在合适范围内,这样在碰撞时才不会因为突然出现的巨大动量差导致物体的速度瞬间出现很大的改变,导致一些不稳定的物理现象发生
优化碰撞矩阵:当两个物体之间发生碰撞时,Unity会首先检查它们所属的Layer是否允许碰撞,这是通过检查碰撞矩阵来实现的,关闭一些层级的碰撞虽然不会有性能的提升,但是可以减少一些因设置错误,而引起的问题

优化物理模块

使用合理的碰撞检测类型
上文提到过Rigidbody有4种类型的碰撞检测,检测精度高的开销也高,但不是所有情况都需要这么高的精度,选择合适的就行
Contact数量是否合理
在性能分析器的物理模块中可以统计当前正在发生碰撞的物体数量,由于碰撞后刚体组件需要计算被力作用的结果,因此当正在发生碰撞的物体很多时cpu就需要花费更多时间去计算,如果场景中看起来没有物体发生碰撞,但是Contact数量又很多时,就需要检查当前时间段是不是某个物体的碰撞框大小设置出了问题,可以使用代码检测主要是哪个物体产生了这么多碰撞


