直觉
乘以Time.deltaTime 用于使瞬时操作以连续(或“平滑”)方式进行。
瞬时操作会导致某个量“跳跃”到不同的值。以下操作是即时的:
Rigidbody.[position|velocity|rotation|angularVelocity] += x;
Rigidbody2D.[position|velocity|rotation|angularVelocity] += x;
Rigidbody.Add[Force|Torque](x, ForceMode.[Impulse|VelocityChange]);
Rigidbody2D.Add[Force|Torque](x, ForceMode2D.Impulse);
以上所有操作都会导致一个数量立即跳转x,与帧的长度无关。
相比之下,以下操作是连续的:
Rigidbody.Add[Force|Torque](x, ForceMode.[Force|Acceleration]);
Rigidbody2D.Add[Force|Torque](x, ForceMode.Force);
-
Rigidbody.velocity = x;(从Rigidbody.position的角度来看是连续的。即设置速度不会使Rigidbody.position突然跳到一个新的值)
所有上述操作都应用于整个框架(至少在概念上会发生这种情况;物理系统在内部执行的操作超出了此答案的范围)。为了说明这一点,Rigidbody.AddForce(1, ForceMode.Force) 将在整个框架内将 1 牛顿的力施加到对象上;位置或速度不会突然跳跃。
那么你什么时候乘以Time.deltaTime?
如果您使用的是连续操作,您几乎绝对应该不乘以Time.deltaTime。但如果您使用的是瞬时操作,答案取决于该操作是否以连续方式使用。
示例 1:爆炸
void Explode() {
rigidbody.velocity += explosionAcceleration;
}
在这里你应该不乘以Time.deltaTime,因为Explode() 只被调用一次。它并不意味着应用于一系列帧。
示例 2:移动
void Update() {
rigidbody.position += velocity * Time.deltaTime;
}
这里你必须乘以Time.deltaTime,因为对象应该随着时间连续移动,而且你还使用了瞬时操作。请注意,这可以替换为连续操作,并且无需乘以Time.deltaTime:
void Update() {
rigidbody.velocity = velocity;
}
虽然这并不完全等同于原始值,因为它忽略了 rigidbody.velocity 的先前值。
示例 3:加速
void Update() {
rigidbody.AddForce(acceleration*Time.deltaTime, ForceMode.VelocityChange);
}
在这里,您应该乘以 Time.deltaTime,因为您希望速度以一致的速率逐帧增加。请注意,这完全等同于:
void Update() {
rigidbody.AddForce(acceleration, ForceMode.Acceleration);
}
此代码在整个帧的过程中应用加速度。这段代码也(在我看来)更干净、更容易理解。
FixedUpdate 呢?
加入FixedUpdate 不会影响上述任何建议。是的,理论上你可以避免乘以Time.deltaTime,因为帧之间的时间总是相同的。但是你所有的单元都必须依赖于固定的帧速率。因此,例如,如果您想以1 m/s 的速率以每秒60 帧的速度移动,则必须将1/60=.01666 添加到每帧的位置。然后想象一下如果你改变你的固定时间步长会发生什么。您将不得不更改一堆常量来适应。
在FixedUpdate 中执行代码不会突然使该代码帧速率独立。您仍然需要采取与Update 相同的预防措施。
附带说明,您不需要在 FixedUpdate 内将 Time.deltaTime 替换为 Time.fixedDeltaTime,因为 Unity 已经进行了这种替换。
结论
如果您希望瞬时操作在一系列帧上平稳运行,请仅乘以 Time.deltaTime。
很多时候,您可以通过使用更直观的连续操作来避免使用Time.deltaTime(参见示例 2 和示例 3)。但这当然取决于上下文。