物理引擎是一套基于牛顿力学的模拟计算系统,它仅仅是对现实物理的一种近似模拟。怎么理解这种“近似”呢?可以从运算精度和时间连续性两个方面理解。
从运算精度上看,物理运动的计算不能无限精确下去,每一次计算都可能隐含着精度的损失,而且这种精度损失可能随着计算的迭代而逐步增大;从时间连续性上看,物理引擎的计算是离散化进行的,每两次计算间隔十几至几十毫秒之久,并不能像真实世界那样保持连续性,这也会导致计算准确性显著下降。
但毕竟,开发游戏用的物理系统的初衷并不是为了进行科学实验和还原现实世界的物理过程,而是为了让游戏能够具备令人信服的物理表现,增强游戏的表现力和用户的沉浸感。只要游戏用户能够感受到游戏世界的重力,例如看到弹跳的篮球并投出完美的三分球,就已经达到目的了。
Rigidbody组件和Rigidbody2D组件是物理系统的重点。刚体是让物体产生物理行为的主要组件。一旦物体挂载了Rigidbody组件,它就被纳入了物理引擎的控制之中,可以受到力的影响并作出反应。
已经被挂载刚体组件的物体,不建议再用脚本直接修改该物体的位置或者直接改变物体的朝向。如果要让物体运动,可以考虑通过对刚体施加作用力来推动物体,然后让物理引擎运算并产生想要的结果;或者直接修改物体的速度(velocity)和角速度(angular velocity),这样比施加作用力更直接。
一些情况下,只需要物体具有Rigidbody组件,但又不能让它的运动受到物理引擎的控制。例如,让角色完全受脚本直接控制,但同时又不让角色被触发器检测到,这种不直接受物理控制的、但用其他方式进行的刚体运动称为运动学(Kinematic)。这种刚体的运动方式虽然部分脱离了物理系统控制而不再受到力的影响,但在需要碰撞检测等情境下依然会被物理系统处理。可以在脚本中随时开启或关闭物体的Is Kinematic选项,但是这样操作会带来一些性能开销,不应频繁使用。
当一个刚体的移动速度和旋转速度已经慢于某个事先定义的阈值,并保持一定时间,那么物理引擎就可以假定它暂时稳定了。在这种情况下,直到它再次受到力的影响之前,物理引擎都不再需要反复计算该物体的运动,这时就可以说该物体进入了“休眠(Sleeping)”状态。这是一种优化性能的方案,“休眠”的物体不会被物理引擎持续更新,从而节约了运算资源,直到它重新被“唤醒”为止。
大多数情况下,刚体的休眠和唤醒都是自动进行的,也就是说开发者不用关心这个细节。但是,总有一些情况物体无法被自动唤醒,这种情况下可能会得到一些奇怪的结果。例如,一个稳定放在地面上且带有Rigidbody组件的物体,在地面被移除后它依然悬挂在空中。如果遇到类似的情况,可以在脚本中主动调用WakeUp方法。
碰撞体组件定义了物体的物理形状。碰撞体本身是隐形的,不一定要和物体的外形完全一致。而且实际上,在游戏制作时会更多使用物体的近似物理形状而不是精确外形,这样可以提升游戏运行效率。
最简单且最节省计算资源的碰撞体,是一系列基本碰撞体。它们包括盒子碰撞体(Box Collider)、球体碰撞体(Sphere Collider)和胶囊碰撞体(Capsule Collider)。在2D物理系统中有类似的2D盒子碰撞体(Box Collider 2D)和2D圆形碰撞体(Circle Collider 2D)。
一个物体上可以同时挂载多个碰撞体组件,这就形成了组合碰撞体(Compound Colliders)。组合碰撞体是一个Unity术语,它不是一个组件,而是一个概念,指的是一个物体挂载了多个碰撞体组件或该物体具有多个碰撞体的子物体。子物体所挂载的碰撞体组件,也会成为父物体物理外形的一部分。
注意,当对物体进行了非等比缩放时,基本碰撞体的范围可能和需要的不一样。这意味着要么可能发生计算错误,要么物体的物理外形和设想的不一致。无论哪种情况都不是好的结果,因此应当避免在非等比缩放的物体上挂载碰撞体组件。
必须模拟碰撞体表面材料的特性,这样碰撞体之间发生交互时才能正确模拟实际的物理效果。例如:冰面上会非常滑,木板在冰面上能够滑很远的距离
在任何力的作用下,体积和形状都不发生改变的物体叫作“刚体(Rigid Body)”。它是力学中的一个抽象概念,即理想模型。事实上任何物体受到外力都不可能不改变形状,现实中的物体都不是真正的刚体。
如今有一些物理系统支持“柔体(Soft Body)”。被定义为柔体的物体,其外形会随着力和加速度的变化而变化,能够逼真还原碰撞发生的具体过程。例如模拟皮球反弹的过程,就可以对皮球受到挤压、产生形变、产生弹力、反弹后形状进一步变化的整个过程进行模拟。这种物理系统目前还没有被广泛地使用,它在表现真实的车辆碰撞损坏、模拟水体或柔软物体方面有着很大的潜力。
碰撞体会默认阻挡刚体的运动,如地板会阻挡小球的下坠。但有时候,需要让物理系统检测两个物体发生重叠,但又不引起物理上的实际碰撞,这时就需要勾选碰撞体组件的Is Trigger属性,将它变成一个触发器。
作为触发器的物体不再是物理上的固体,反而允许其他物体随意从中穿过。当另一个碰撞体进入了触发器的范围,就会调用脚本的OnTriggerEnter()方法。但要特别注意两个物体必须至少有一个带有刚体组件(可以是动力学刚体),否则无法触发脚本。
当两个物体发生接触时,因物体的具体设置不同,某些情况下会产生碰撞或触发事件,而另一些情况下不会产生碰撞或触发事件。
对包含了碰撞体组件的物体来说,物体上是否具有刚体组件,以及刚体组件上动力学设置的不同,都会使物体的物理碰撞特性产生变化。可以将所有的碰撞体分为静态碰撞体、刚体碰撞体和动力学刚体碰撞体3类。
没有挂载刚体组件的碰撞体,称为静态碰撞体。静态碰撞体通常用于制作关卡中固定的部分,例如地形和障碍物一般不会移动位置,在刚体碰撞到它们的时候,它们的位置也不会变化。
挂载了普通刚体组件的碰撞体,称为刚体碰撞体。物理引擎会一直模拟计算刚体碰撞体的物理状态,因此刚体碰撞体会对碰撞以及脚本施加的力做出反应。
挂载了刚体组件且刚体组件设置为动力学刚体的碰撞体,称为动力学刚体碰撞体。可以在脚本中直接修改动力学刚体碰撞体的位置来移动它,但它并不会对碰撞、力和速度的变化做出反应。动力学刚体碰撞体通常用在需要改变位置或状态的碰撞体上,如一个可以滑动的门,大部分时间门就与静止的障碍物一样,但是在必要的时候可以打开。