首页 > 解决方案 > 与帧率无关的 FixedUpdate 与 Update

问题描述

我已经阅读了这个和官方文档:fixedUpdate()深入的解释

所以我试图分离我的代码。首先,在 中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 QA 看到了我的问题,他们的回答是:

您正在添加 Force 依赖于 fixedDeltaTime ,这取决于您的可用性能(或本质上的帧速率)。

如果您转到 Edit->Project Settings->Time 并将固定 Timestep 更改为更大的值,您将获得预期的行为。尝试几个不同的 Fixed Timestep 值,看看行为如何变化。

另一个建议是重写代码,使其不依赖于帧速率(例如,使用 Velocity 而不是强制或在跳转时添加特定量的力,而不依赖于 Timestep)。

“另一个建议是重写代码,使其不依赖于帧速率” - >你会怎么做,我认为我上面的代码正在这样做!

我错过了什么?我做错了什么/可能是什么解决方案?

标签: unity3d

解决方案


我想知道给你这个答案的那个 Unity QA 是谁,这太令人畏惧了。

让我解释一下现实中会发生什么。

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

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

例如,假设fixedDeltaTime = 0.0166667自上次物理循环以来经过的时间小于该时间,例如10ms。Unity 不会执行物理循环。现在假设甚至下一帧已经在10ms 内渲染 - 这意味着自上一次物理循环以来,20ms 已经过去。由于这高于fixedDeltaTime,Unity 将执行一个物理循环。有时可能会发生帧渲染非常缓慢(由于不可预见的原因),例如以40毫秒为单位。为了保持物理模拟的一致性,Unity 必须连续运行两个物理循环,这是因为0.04/0.0166667 = 2.4.

请记住,Unity 会跟踪上一次物理循环开始时间和下一次之间的差异:如果渲染10每帧持续 ms,并且fixedDeltaTime设置为166667ms (60Hz),则一旦启动运行时,Unity 就会执行第一个物理循环,然后在第一个渲染帧之后跳过一个(因为只有10ms 已经过去而不是166667),然后在第二个渲染帧之后执行一个物理循环(20ms 通过反对166667)。但是现在我们的循环被3.3333ms 去同步了,所以 Unity 会跟踪它。

在第 3 帧之后,又10过了一毫秒,但不会执行任何物理循环,因为10+3.3333 = 13.3333它仍然低于fixedDeltaTime. 现在,让我们假设第 4 个渲染帧“出错”,并且持续时间25为 ms 而不是10. 在下一个 Physics 循环开始时,25+13.3333 = 38.3333自上次 Physics 更新以来总共经过了38.3333/16.6667 = 2.3,并且 Unity 将连续执行两个Physics 循环以跟上固定步长模拟,然后继续渲染第 5 帧。


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

在某个时刻,您执行Update(),并设置_jumpRequest = true;_fallRequest = true;

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

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

Update()再次执行时,最后你的代码设置_keepOnJumping = true;,当它回到FixedUpdate()它时将执行AddForce ForceMode.Acceleration,但紧接着,第二次执行另一个物理模拟(由于低帧率),再次将刚体向下拖动到它之前可以在屏幕上渲染。

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


推荐阅读