在Unity中什么时候乘以Time.deltaTime?

When do you multiply by Time.deltaTime in Unity?

关于何时应该或不应该将数量乘以 Time.deltaTime,似乎有很多 confusing/conflicting 建议。所以我想知道,因为它属于 Unity 物理系统,你什么时候应该乘以 Time.deltaTime

我知道Time.deltaTime是用来让某些动作与帧率无关的,但有些情况下并不清楚动作是否与帧率无关。例如:

我已经看到针对个别情况的解决方案,但如果有一种简单的直觉可以解决所有这些情况,那就太好了。

直觉

乘以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,因为帧之间的时间总是相同的。但是你所有的单位都必须依赖于固定的帧率。因此,例如,如果您想以每秒 60 帧的 1 m/s 速率移动,则必须将 1/60=.01666 添加到每帧的位置。然后想象如果你改变你的固定时间步长会发生什么。您将不得不更改一堆常量以适应。

执行 FixedUpdate 中的代码不会突然使该代码帧率独立。您仍然必须采取与 Update.

中相同的预防措施

附带说明一下,您不需要在 FixedUpdate 中将 Time.deltaTime 替换为 Time.fixedDeltaTime,因为 Unity 已经进行了此替换。

结论

如果您希望瞬时操作在一系列帧上平稳运行,则只乘以 Time.deltaTime

很多时候,您可以通过使用连续操作来避免使用Time.deltaTime,这通常更直观(参见示例 2 和 3)。但这当然取决于上下文。

考虑这个问题的一个简单方法是确定:

对象的变换是直接操作的吗?

当对象通过其 transform.positiontransform.rotation 组件直接移动时,变换操作应发生在 Update() 中,每个绘制帧运行一次。开发人员在构建游戏时应期望 Update/frame 速率在零和游戏的最大帧速率之间变化。实现这一点的方法是将现在和上一帧之间的时间考虑到每个 update/frame.

的移动(步骤)中

因此,对 transform.positiontransform.rotation 的操作应该 影响 Time.deltaTime 并且应该发生在 Update() 内。 =31=]

物体是否被物理操纵?

在这种情况下,应通过在 FixedUpdate() 中调用 Rigidbody.AddForce()Rigidbody.AddTorque() 来施加力和扭矩。 FixedUpdate() 预计会以 fixed/guaranteed 的速率发生,Unity 默认为每秒 50 次(这可以在游戏项目的物理设置中进行调整)。

使事情稍微复杂一点,当使用 ForceMode.Force(默认)或 ForceMode.Acceleration 调用 AddForce()AddTorque() 时,为 [=25= 提供的值] 或 torque 参数将被解释为一整秒的量,因此这些函数将调整一个固定时间步长(即 x 0.02)的值(force/torque 或 acceleration/angular 加速度)使用默认的 Unity Physics 固定更新率。

如果您对 ForceMode.ImpulseForceMode.VelocityChange 感到好奇,这些会将 forcetorque 解释为在该固定时间步长内应用的金额(即该值不会随时间调整。