独立于帧速率的FixedUpdate与Update

时间:2019-03-08 15:12:15

标签: unity3d

我已经阅读了this和官方文档:fixedUpdate()deep explanation

所以我试图分离我的代码。首先,在Update()中,我没有给出完整的代码,这些变量是不言自明的:

private void Update()
{
    if (Input.GetButton("Jump")) {
        if (groundsTouched>0) {
            _jumpRequest = true;
        } else {
            _keepOnJumping = true;
        }
    } else {
        _keepOnJumping = false;
    }
    /* Handle release button: */
    _fallRequest = true;
}

现在我像这样在FixedUpdate()中进行所有计算:

private void FixedUpdate()
{
    if (_jumpRequest) {
        if (!_jumpGravitySent) {
            _jumpGravitySent = true;
            _animator.SetBool("Jump", true);
            _jumpRequest = false;
            jumpTimeCounter = jumpTime;
            /* Cancel all force (couldn't find a better way) */
            _rigidbody.velocity = Vector3.zero;
            _rigidbody.angularVelocity = Vector3.zero;
            _rigidbody.AddForce(
                Vector3.up * jumpVelocity, ForceMode.VelocityChange
            );
        }
    } else if (_keepOnJumping) {
        jumpTimeCounter -= Time.fixedDeltaTime;
        if (jumpTimeCounter >= 0) {
            _rigidbody.AddForce(
                Vector3.up * jumpVelocity * jumpKeepMultiplier, 
                ForceMode.Acceleration
            );
        }
    }
    if (groundsTouched == 0 && 
        _rigidbody.velocity.y > velocityFallMin &&
        _rigidbody.velocity.y < velocityFallMax
    ) {
        _animator.SetBool("Jump", false);
        _animator.SetBool("Fall", true);
    }
    if (_fallRequest) {
        _fallRequest = false;
        _jumpGravitySent = false;
        _keepOnJumping = false;
    }
}

我遇到的问题真的很奇怪:当FPS较低时,玩家无法跳高。

Unity质量检查人员发现了我的问题,他们的回答是:

  

您要添加的力取决于fixedDeltaTime,后者取决于   您的可用性能(或本质上是帧速率)。

     

如果转到“编辑”->“项目设置”->“时间”并将固定的“时间步长”更改为   较大的值,您将获得预期的行为。尝试一些   固定时间步长的不同值,并查看行为如何变化。

     

另一个建议是重写代码,使其不依赖   帧速率(例如,使用“力度”而不是强制或添加特定量)   的力量,而不取决于时间步长。

“另一个建议是重写代码,使其不依赖于帧频”->您将如何做,我以为上面的代码正在这样做!

我想念什么?我在做什么错/解决方案是什么?

1 个答案:

答案 0 :(得分:1)

我想知道是谁给了您这个答案的Unity QA,这很令人毛骨悚然。

让我解释一下实际发生的情况

1)让我们从fixedDeltaTime开始:此值从不取决于帧速率。可以在编辑器中(在Edit->Project Settings->Time中进行设置,这是在运行时保留的值,除非任何脚本都会通过赋值对其进行更改。 Unity引擎永远不会对其进行更改。

2)物理循环:在完整的引擎循环中,Unity将执行多个物理循环(0,1或更多),然后执行一个渲染循环。每个Rendering循环执行的Physics循环数基于fixedDeltaTime以及自上一个循环以来经过的时间(即:Rendering循环的deltaTime)。

例如,假设fixedDeltaTime = 0.0166667,并且自上次物理循环以来经过的时间小于10 ms。 Unity将不会执行物理循环。现在假设甚至下一帧都以10 ms渲染-这意味着自从上一次物理循环以来,20 ms已经过去。由于此值比fixedDeltaTime高,因此Unity将执行物理循环。 有时可能会发生帧渲染速度非常慢的情况(由于不可预见的原因),例如40 ms。为了保持物理模拟的一致性,Unity必须连续运行 two 物理循环,这是因为0.04/0.0166667 = 2.4

请记住,Unity会跟踪上一个Physics循环的开始时间与下一个Physics循环的开始时间之间的差异:如果渲染每帧持续10 ms毫秒,并且fixedDeltaTime设置为{{1 }} ms(60Hz),一旦启动运行时,Unity将执行第一个Physics循环,然后在第一个渲染帧后跳过一个(因为仅经过166667 ms而不是10) ,然后在第二个渲染帧(针对166667传递20 ms之后执行一个“物理”循环。但是现在我们有了166667 ms不同步的循环,因此Unity会对此进行跟踪。

在第3帧之后,又经过了3.3333毫秒,但是由于10仍然低于10+3.3333 = 13.3333,因此不会执行任何物理循环。现在,让我们假设第4个渲染帧“出错”,并且持续fixedDeltaTime毫秒而不是25。在下一个Physics循环开始时,自上次Physics更新以来总共经过了1025+13.3333 = 38.3333,Unity将连续执行 two Physics循环以在渲染第5帧之前,请紧跟固定步长仿真。


所有介绍之后,让我们回到您的问题,看看会发生什么:

在特定时刻执行38.3333/16.6667 = 2.3,并设置Update()_jumpRequest = true;

在此渲染帧之后,第一次执行_fallRequest = true;,执行FixedUpdate()行,并设置AddForce ForceMode.VelocityChange_fallRequest = false;_jumpGravitySent = false;_keepOnJumping = false;结束后,Unity将执行物理模拟,并借助物理引擎来调整刚体的位置和速度。

现在问题触发了:由于渲染帧很慢,因此物理循环至少连续执行两次,但是在两者之间没有执行FixedUpdate(),因此Update()中的所有内容都会被跳过,但是物理模拟是第二次运行,将刚体位置相对于预期的最高位置向下拖动。

再次执行FixedUpdate()时,最终您的代码设置了Update(),当它返回到_keepOnJumping = true;时,它将执行FixedUpdate(),但紧接着又执行另一个第二次执行物理模拟(由于低帧频),在将刚体显示到屏幕上之前再次将其向下拖动。

希望这有助于您了解问题及其发生原因,以便您现在拥有正确解决问题的正确工具。