在Unity中什么时候乘以Time.deltaTime?
When do you multiply by Time.deltaTime in Unity?
关于何时应该或不应该将数量乘以 Time.deltaTime
,似乎有很多 confusing/conflicting 建议。所以我想知道,因为它属于 Unity 物理系统,你什么时候应该乘以 Time.deltaTime
?
我知道Time.deltaTime是用来让某些动作与帧率无关的,但有些情况下并不清楚动作是否与帧率无关。例如:
- 在 FixedUpdate 中执行
rigidbody.velocity += acceleration*Time.deltaTime;
。由于 FixedUpdate 以恒定时间步长运行,这是否不需要乘以 Time.deltaTime
?
- 正在做
rigidbody.AddForce(force*Time.deltaTime, forceMode);
。各种 ForceMode
对此有何影响?
我已经看到针对个别情况的解决方案,但如果有一种简单的直觉可以解决所有这些情况,那就太好了。
直觉
乘以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.position
和 transform.rotation
组件直接移动时,变换操作应发生在 Update()
中,每个绘制帧运行一次。开发人员在构建游戏时应期望 Update/frame 速率在零和游戏的最大帧速率之间变化。实现这一点的方法是将现在和上一帧之间的时间考虑到每个 update/frame.
的移动(步骤)中
因此,对 transform.position
和 transform.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.Impulse
或 ForceMode.VelocityChange
感到好奇,这些会将 force
或 torque
解释为在该固定时间步长内应用的金额(即该值不会随时间调整。
关于何时应该或不应该将数量乘以 Time.deltaTime
,似乎有很多 confusing/conflicting 建议。所以我想知道,因为它属于 Unity 物理系统,你什么时候应该乘以 Time.deltaTime
?
我知道Time.deltaTime是用来让某些动作与帧率无关的,但有些情况下并不清楚动作是否与帧率无关。例如:
- 在 FixedUpdate 中执行
rigidbody.velocity += acceleration*Time.deltaTime;
。由于 FixedUpdate 以恒定时间步长运行,这是否不需要乘以Time.deltaTime
? - 正在做
rigidbody.AddForce(force*Time.deltaTime, forceMode);
。各种ForceMode
对此有何影响?
我已经看到针对个别情况的解决方案,但如果有一种简单的直觉可以解决所有这些情况,那就太好了。
直觉
乘以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.position
和 transform.rotation
组件直接移动时,变换操作应发生在 Update()
中,每个绘制帧运行一次。开发人员在构建游戏时应期望 Update/frame 速率在零和游戏的最大帧速率之间变化。实现这一点的方法是将现在和上一帧之间的时间考虑到每个 update/frame.
因此,对 transform.position
和 transform.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.Impulse
或 ForceMode.VelocityChange
感到好奇,这些会将 force
或 torque
解释为在该固定时间步长内应用的金额(即该值不会随时间调整。