When do you multiply by Time.deltaTime in Unity?

Eri*_*ast 3 unity-game-engine game-physics

关于何时应该或不应该将数量乘以 ,似乎有很多令人困惑/矛盾的建议Time.deltaTime。所以我想知道,因为它与 Unity 物理系统有关,你应该在Time.deltaTime什么时候乘以

我知道 Time.deltaTime 用于使某些动作与帧速率无关,但在某些情况下,不清楚动作是否与帧速率无关。例如:

  • rigidbody.velocity += acceleration*Time.deltaTime;在 FixedUpdate 中做。由于 FixedUpdate 以恒定时间步长运行,这是否使乘法变得Time.deltaTime不必要?
  • rigidbody.AddForce(force*Time.deltaTime, forceMode);。各种ForceModes 对此有何影响?

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

Eri*_*ast 11

直觉

乘以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;
}
Run Code Online (Sandbox Code Playgroud)

在这里你应该乘以Time.deltaTime因为只Explode()被调用一次。它并不意味着应用于一系列帧。

示例 2:移动

void Update() {
    rigidbody.position += velocity * Time.deltaTime;
}
Run Code Online (Sandbox Code Playgroud)

在这里,您确实必须乘以,Time.deltaTime因为对象应该随着时间的推移连续移动,并且您还使用了瞬时操作。请注意,这可以用连续操作代替,并且不需要乘以Time.deltaTime

void Update() {
    rigidbody.velocity = velocity;
}
Run Code Online (Sandbox Code Playgroud)

尽管这并不完全等同于原始值,因为它忽略了rigidbody.velocity.

示例 3:加速

void Update() {
    rigidbody.AddForce(acceleration*Time.deltaTime, ForceMode.VelocityChange);
}
Run Code Online (Sandbox Code Playgroud)

在这里,您应该乘以 ,Time.deltaTime因为您希望速度以一致的速率逐帧增加。请注意,这完全等同于:

void Update() {
    rigidbody.AddForce(acceleration, ForceMode.Acceleration);
}
Run Code Online (Sandbox Code Playgroud)

此代码在整个帧的过程中应用加速度。这段代码(在我看来)也更清晰、更容易理解。

怎么样FixedUpdate

入住FixedUpdate不会影响上述任何建议。是的,理论上你可以从不乘以逃脱,Time.deltaTime因为帧之间的时间总是相同的。但是你所有的单位都必须依赖于固定的帧速率。因此,举例来说,如果你想的速率移动1 m/s60帧每秒,你就必须添加1/60=.01666到每一帧的位置。然后想象一下如果你改变你的固定时间步长会发生什么。您将不得不更改一堆常量以适应。

执行代码FixedUpdate不会突然使代码帧率独立。您仍然必须采取与 中相同的预防措施Update

作为旁注,您不需要Time.deltaTimeTime.fixedDeltaTimeinside替换,FixedUpdate因为 Unity 已经进行了此替换。

结论

Time.deltaTime当您希望瞬时操作在一系列帧上平滑运行时才乘以。

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