本文主要讨论,在Unity中使用物理引擎Physics2D的三个方面的内容:
最直接的方法,就是设置Rigidbody2D的Interpolate属性,面板属性提供了下拉选项,有None,Interpolate,Extrapolate。官方文档介绍的很清晰,这是物理模拟在Update过程中的运动插值计算,用来解决运动生涩(卡顿)的问题。
经过测试发现,如果Rigidbody2D的所有的父节点是固定不动的(没有手动控制),Interpolate 和 Extrapolate 一样顺滑,并且要比None来的流畅。如果Rigidbody2D的父节点有运动(手动控制),Interpolate会有明显的卡顿,Extrapolate 和 None 差不多,但是依然是不流畅的。
抛开性能问题,我遇到的卡顿来自于两个原因。第一,就是上面所说的Rigidbody2D的父节点发生了运动,造成了物理运动有明显的生涩感。第二,就是手动设置了Rigidbody2D的transform属性,如Position,Rotation,Scale等。
其实,这两个问题是同一个原因,就是不要手动设置和Rigidbody2D相关的transform属性,包括父类链的所有节点。物理引擎会接管一切运动模拟,引擎会有自己的算法预测,手动设置会造成冲突。
经过测试发现,哪怕使用了Rigidbody2D的Constraints约束,Freeze Position 或 Freeze Rotation 之后,依然不能去手动设置锁住的属性,否则依旧会造成卡顿。
那如果遇到了必须要手动设置的需求,应该怎么办呢 ? 这时候可以使用Rigidbody2D暴露的接口去控制。
Rigidbody2D.position
Rigidbody2D.rotation
Rigidbody2D.MovePosition
Rigidbody2D.MoveRotation
Rigidbody2D 暴露了两个属性和两个方法,用来设置position和rotation。经过测试发现,设置属性依然卡顿,但使用Move方法就消除了卡顿的现象。所以,手动改变Rigidbody2D的position和rotation使用其暴露的Move方法是最好的办法。
在使用Rigidbody2D的过程中,期望能够实现,因为重心在运动中产生自发旋转的效果,比如弓箭的飞行模拟。
开始使用了centerOfMass,强行把刚体的质心移动到物体边缘或外部,期望质心能够在重力作用下,形成旋转力,结果不尽人意,自由落体的时候作用很小,反而碰撞接触了产生了很大的效用。
后来,使用了Rigidbody2D的angularVelocity角速度设置,并且Freeze Rotation锁住了刚体本身的旋转模拟,让angularVelocity无干扰的发挥作用。其结果是可以工作,但是效果不够真实,angularVelocity并没有衰减而且是一个固定的速率,难道还需要每帧去模拟angularVelocity的变化,太过繁琐。
最终,使用了每帧去计算rotation的数值,并Freeze Rotation锁住旋转,得到了很好的效果。大概实现如下:
private void FixedUpdate()
{
if (this.body2D.bodyType == RigidbodyType2D.Dynamic)
{
var deltaTime = Time.fixedDeltaTime * 0.5f * this.transform.localScale.x > 0 ? 1.0f : -1.0f;
var v2 = this.body2D.velocity;
// s = (v0 + v1) * t / 2
this.body2D.MoveRotation(Mathf.Atan2(v2.y * deltaTime, v2.x * deltaTime) * Mathf.Rad2Deg);
}
}
根据当前刚体的速度,计算一帧的xy方向的运动距离,通过Atan2来计算出旋转角度,在FixedUpdate中手动设置给刚体,当然要使用MoveRotation来得到流畅的旋转效果。
「Extrapolate」