【问题标题】:When do you multiply by Time.deltaTime in Unity?你什么时候乘以 Unity 中的 Time.deltaTime?
【发布时间】:2022-01-17 14:01:35
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: unity3d game-physics


    【解决方案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.positiontransform.rotation 组件移动对象时,应在Update() 中进行转换操作,该操作在每个绘制帧运行一次。开发者在构建游戏时应该期望更新/帧速率在零和游戏的最大帧速率之间变化。实现这一点的方法是将现在和上一帧之间的时间考虑到每个更新/帧的移动(步骤)中。

      因此,对transform.positiontransform.rotation 的操作应该考虑到Time.deltaTime 并且应该发生在Update() 内。

      物体是否被物理操纵?

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

      为了使事情复杂一点,当使用 ForceMode.Force(默认)或 ForceMode.Acceleration 调用 AddForce()AddTorque() 时,为 forcetorque 参数提供的值将被解释为一整秒,因此这些函数将调整一个固定时间步长的值(力/扭矩或加速度/角加速度)(即,默认 Unity Physics 固定更新速率的 x 0.02 秒)。

      如果您对ForceMode.ImpulseForceMode.VelocityChange 感到好奇,它们会将forcetorque 解释为在该固定时间步内应用的数量(即该值不会随时间调整)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-12-08
        • 1970-01-01
        • 1970-01-01
        • 2010-09-19
        • 2012-04-23
        • 2018-01-17
        相关资源
        最近更新 更多