FixedUpdate vs Update, независимый от частоты кадров - PullRequest
1 голос
/ 08 марта 2019

Я уже прочитал это и официальную документацию: 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, которое зависит от ваша доступная производительность (или частота кадров по существу).

Если вы перейдете в «Правка» -> «Настройки проекта» -> «Время» и измените фиксированный временной шаг на большее значение, вы получите ожидаемое поведение. Попробуйте несколько различные значения Fixed Timestep и посмотрите, как меняется поведение.

Еще одно предложение - переписать код, чтобы он не зависел на частоте кадров (например, использовать скорость, а не форсировать или добавить определенную сумму силы при прыжке, не зависит от временного шага).

«Другим предложением было бы переписать код, чтобы он не зависел от частоты кадров» -> как бы вы это сделали, я подумал, что мой код выше делает это!

Что мне не хватает? Что я делаю не так / что может быть решением?

1 Ответ

1 голос
/ 09 марта 2019

Интересно, кто такой Unity QA, который дал вам этот ответ, он заслуживает доверия.

Позвольте мне объяснить, что происходит, на самом деле .

1) Давайте начнем с fixedDeltaTime: , это значение НИКОГДА не зависит от частоты кадров . Его можно установить в редакторе (в Edit->Project Settings->Time), и это значение сохраняется во время выполнения, , если какой-либо сценарий не изменит его с помощью присвоения. Движок Unity сам по себе не меняет его.

2) Цикл физики: в полном цикле движка Unity выполнит несколько циклов физики (0,1 или более), а затем один цикл рендеринга. Количество физических циклов, выполненных на цикл рендеринга, основано на том fixedDeltaTime и сколько времени прошло с момента последнего (т. Е. * deltaTime цикла рендеринга).

Например, скажем, fixedDeltaTime = 0.0166667, а время, прошедшее с последнего цикла по физике, меньше, чем, скажем, 10 мс. Unity не будет выполнять цикл Physics. Теперь предположим, что даже следующий кадр был обработан за 10 мс - это означает, что с момента последнего цикла по физике прошло 20 мс. Поскольку это больше, чем fixedDeltaTime, Unity выполнит цикл по физике. Иногда может случиться, что кадр отображается очень медленно (по непредвиденным причинам), например, за 40 мс. Чтобы поддерживать симуляцию физики согласованной, Unity должна запустить два цикла физики подряд, потому что 0.04/0.0166667 = 2.4.

Имейте в виду, что Unity отслеживает разницу между временем начала последнего цикла Physics и следующим: если рендеринг длится 10 мс каждый кадр, а fixedDeltaTime установлен на 166667 мс (60 Гц) , как только вы запустите среду выполнения, Unity выполнит первый цикл по физике, затем пропустит один после 1-го кадра рендеринга (поскольку вместо 166667 прошло только 10 мс), затем выполнит один цикл по физике после 2-го рендеринга кадр (20 мс передано против 166667). Но теперь у нас есть цикл, десинхронизированный на 3.3333 мс, поэтому Unity будет отслеживать это.

После 3-го кадра прошло еще 10 мс, но цикл Физики не будет выполняться, начиная с 10+3.3333 = 13.3333, который все еще ниже, чем fixedDeltaTime. Теперь давайте предположим, что 4-й кадр рендеринга "идет не так" и длится 25 мс вместо просто 10. В начале следующего цикла по физике с момента последнего обновления физики прошло 25+13.3333 = 38.3333, а 38.3333/16.6667 = 2.3, и Unity выполнит два цикла по физике подряд, чтобы не отставать от фиксированного пошаговое моделирование, прежде чем приступить к визуализации 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, но сразу после этого, во второй раз выполняется другое физическое моделирование (из-за низкой частоты кадров), перетаскивая жесткое тело вниз, прежде чем его можно будет отобразить на экране.

Надеюсь, что это поможет понять вашу проблему и почему это происходит, так что теперь у вас есть нужные инструменты, чтобы исправить ее правильно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...