你啥时候乘以 Unity 中的 Time.deltaTime?
Posted
技术标签:
【中文标题】你啥时候乘以 Unity 中的 Time.deltaTime?【英文标题】:When do you multiply by Time.deltaTime in Unity?你什么时候乘以 Unity 中的 Time.deltaTime? 【发布时间】:2020-08-26 19:07:48 【问题描述】:关于何时应该或不应该将数量乘以 Time.deltaTime
,似乎有很多令人困惑/相互矛盾的建议。所以我想知道,因为它与 Unity 物理系统有关,你应该什么时候乘以 Time.deltaTime
?
我了解 Time.deltaTime 用于使某些动作与帧速率无关,但在某些情况下,不清楚某个动作是否与帧速率无关。例如:
在 FixedUpdate 中执行rigidbody.velocity += acceleration*Time.deltaTime;
。由于 FixedUpdate 以恒定的时间步长运行,这是否使乘以 Time.deltaTime
变得不必要?
正在做rigidbody.AddForce(force*Time.deltaTime, forceMode);
。各种ForceMode
s 对此有何影响?
我已经看到了针对个别情况的解决方案,但如果有一种简单的直觉可以解决所有这些情况,那就太好了。
【问题讨论】:
【参考方案1】:直觉
乘以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)。但这当然取决于上下文。
【讨论】:
【参考方案2】:考虑这个问题的一个简单方法是确定:
是否直接操作对象的变换?
当直接通过其transform.position
和transform.rotation
组件移动对象时,应在Update()
中进行转换操作,该操作在每个绘制帧运行一次。开发者在构建游戏时应该期望更新/帧速率在零和游戏的最大帧速率之间变化。实现这一点的方法是将现在和上一帧之间的时间考虑到每个更新/帧的移动(步骤)中。
因此,对transform.position
和transform.rotation
的操作应该考虑到Time.deltaTime
并且应该发生在Update()
内。
物体是否被物理操纵?
在这种情况下,应通过在FixedUpdate()
中调用Rigidbody.AddForce()
或Rigidbody.AddTorque()
来施加力和扭矩。 FixedUpdate()
可以预期以固定/保证的速率发生,Unity 默认为每秒 50 次(可以在游戏项目的物理设置中进行调整)。
为了使事情复杂一点,当使用 ForceMode.Force
(默认)或 ForceMode.Acceleration
调用 AddForce()
和 AddTorque()
时,为 force
或 torque
参数提供的值将被解释为一整秒,因此这些函数将调整一个固定时间步长的值(力/扭矩或加速度/角加速度)(即,默认 Unity Physics 固定更新速率的 x 0.02 秒)。
如果您对ForceMode.Impulse
或ForceMode.VelocityChange
感到好奇,它们会将force
或torque
解释为在该固定时间步内应用的数量(即该值不会随时间调整)。
【讨论】:
以上是关于你啥时候乘以 Unity 中的 Time.deltaTime?的主要内容,如果未能解决你的问题,请参考以下文章