有群友在自己的游戏开发过程中遇到了一个问题,即使将Collider的isTrigger属性设置为True,并且在一次碰撞后取消掉对应的碰撞(利用Unity的Physics.IgnoreCollision方法), 即确保了碰撞发生的单一性,在性能测试中发现仍然会有大量的Physics.FindNewContacts操作(如下图),而这成为了影响游戏运行效率的最大开销。
通过搜索,我们在Unity的Discussion社区中寻找到了下面两篇求助帖子,并都得到了Unity Staff的回复
其中在第一篇帖子中,摘引了Unity Physics2D官方文档中的语句,并将问题成功导向至Move操作上
A broadphase update occurs when physics shapes are added, removed or change in size.
这表明了,仅当物理形状被添加、移除或改变形体时,才会触发一次物理更新的广播,也即重新计算整个物理系统
而Move操作并不在其中,也就是正确的Move操作并不会导致物理系统被过多次的重算,导致高的性能开销
但在第一篇帖子中,Unity Staff并没有给出最终的解决方案,让我们将视角放在第二篇帖子上
在第二篇帖子中,Staff给出了一个非常简单扼要的解决方案:
The only thing in physics that actually moves is a Rigidbody2D and you always use its API and never, ever the Transform.
没错,这就是问题的所在:对于有Collider的物理体,应当使用Rigidbody2D组件的MovePosition()函数或设置velocity属性,来达成移动的目的,而不是直接通过Transform进行坐标设置(即使没有Rigidbody2D组件也应当进行添加)
而Staff也给出了这个问题的成因:
But repeating what I said above, you never ever move anything in physics by modifying the Transform; you use the Rigidbody2D API.
Without a Rigidbody2D, modifying the Transform position/rotation causes the colliders in question are recreated from scratch
也就是说,当我们通过Transform的position/rotation对物理体的位置/旋转属性进行更改时,物理体(如各种Coliider)的position或rotation属性并不会被直接更改,相反,这些物理组件会被重新创建,再结合上面关于物理重新计算的情况,就可以很明确地知晓其导致大量性能开销的原因。
另外,当改用Rigidbody2D更换位置时,其会从每一帧的运算调整为再fixedUpdate中运算,这将被Time中的属性所决定,运动的动效会不太顺滑。
那么,为了解决这个问题,可以调整Rigidbody2D Interpolate属性为Interpolate或Extrapolate
Interpolate 运动插值基于前几帧的位置。在一个相对平稳的运动环境里是有很好效果的。但如果是颠簸,崎岖的运动环境,前几帧的插值并不能很好的反应未来的趋势。
Extrapolate 运动插值基于后几帧的预测位置。根据物理公式和地形预测,可以很好的估算出未来几帧的情况,但遇到突然的碰撞或外力影响,就无法得到很好的效果。