「Unity3D」(8)Rigidbody2D卡顿问题和重心旋转模拟

本文主要讨论,在Unity中使用物理引擎Physics2D的三个方面的内容:

  • 如何让Rigidbody2D物理模拟看起来更加的顺滑。
  • 介绍几个造成运动卡顿的原因和解决方法。
  • 针对Rigidbody2D的重心旋转模拟。

让运动更加顺滑

最直接的方法,就是设置Rigidbody2D的Interpolate属性,面板属性提供了下拉选项,有None,Interpolate,Extrapolate。官方文档介绍的很清晰,这是物理模拟在Update过程中的运动插值计算,用来解决运动生涩(卡顿)的问题。

  • None 不进行平滑插值。
  • Interpolate 运动插值基于前几帧的位置。在一个相对平稳的运动环境里是有很好效果的。但如果是颠簸,崎岖的运动环境,前几帧的插值并不能很好的反应未来的趋势。
  • Extrapolate 运动插值基于后几帧的预测位置。根据物理公式和地形预测,可以很好的估算出未来几帧的情况,但遇到突然的碰撞或外力影响,就无法得到很好的效果。

经过测试发现,如果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」

你可能感兴趣的:(Unity3D)