菜单

ji233
发布于 2024-09-14 / 138 阅读
2
0

记一次Physics.FindNewContacts操作占用过多的优化经历

有群友在自己的游戏开发过程中遇到了一个问题,即使将Collider的isTrigger属性设置为True,并且在一次碰撞后取消掉对应的碰撞(利用Unity的Physics.IgnoreCollision方法), 即确保了碰撞发生的单一性,在性能测试中发现仍然会有大量的Physics.FindNewContacts操作(如下图),而这成为了影响游戏运行效率的最大开销。

通过搜索,我们在Unity的Discussion社区中寻找到了下面两篇求助帖子,并都得到了Unity Staff的回复

https://discussions.unity.com/t/physics2d-findnewcontacts-being-called-when-moving-rigidbodies-with-moveposition/934057

https://discussions.unity.com/t/moving-a-large-amount-of-2d-colliders-profiler-physics2d-findnewcontacts/927845

其中在第一篇帖子中,摘引了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 运动插值基于后几帧的预测位置。根据物理公式和地形预测,可以很好的估算出未来几帧的情况,但遇到突然的碰撞或外力影响,就无法得到很好的效果。


评论