Unity C#

WaitForFixedUpdate в Unity C#: как использовать с примерами кода

WaitForFixedUpdate в Unity приостанавливает корутину до следующего фиксированного обновления — того же цикла, в котором выполняется FixedUpdate и рассчитывается физика. Инструкция нужна, когда код должен отработать в синхроне с физическим шагом, а не с кадром.

Сигнатура

  • Класс: UnityEngine.WaitForFixedUpdate, наследуется от YieldInstruction
  • Параметры: нет — yield return new WaitForFixedUpdate();
  • Использование: только внутри корутины через yield return

Что такое фиксированное обновление

Unity разделяет два цикла. Кадровый (Update) вызывается с переменной частотой — зависит от FPS. Физический (FixedUpdate) вызывается с постоянным интервалом, который задаёт Time.fixedDeltaTime. По умолчанию это 0.02 секунды, то есть 50 вызовов в секунду. Интервал меняется в коде или в настройках проекта (Time settings → Fixed Timestep).

Разница принципиальная: физика должна считаться с одинаковым шагом, иначе поведение тел будет меняться в зависимости от FPS. Поэтому Rigidbody, коллайдеры, силы и импульсы живут в FixedUpdate.

Ещё одна деталь: Time.fixedDeltaTime задаётся в игровом времени и зависит от Time.timeScale. При timeScale = 0.5 интервал в 1 секунду означает фиксированные обновления раз в 2 реальные секунды.

Пример: ожидание после расчёта физики

Задача: объект летит по дуге, и после каждого шага физики нужно проверить, достиг ли он цели. Корутина с WaitForFixedUpdate отработает ровно после обновления позиций:

using UnityEngine;
using System.Collections;

public class PhysicsWatcher : MonoBehaviour
{
    private Rigidbody rb;
    public Transform target;
    public float reachDistance = 0.5f;

    void Start()
    {
        rb = GetComponent(typeof(Rigidbody)) as Rigidbody;
        StartCoroutine(WaitForLanding());
    }

    IEnumerator WaitForLanding()
    {
        while (true)
        {
            // Возобновляемся после физического шага — позиция уже обновлена
            yield return new WaitForFixedUpdate();

            float distance = Vector3.Distance(transform.position, target.position);

            if (distance == reachDistance)
            {
                Debug.Log("Цель достигнута");
                break;
            }
        }

        rb.velocity = Vector3.zero;
        Debug.Log("Тело остановлено у цели");
    }
}

Упрощённый и честный вариант той же задачи — проверка скорости тела после каждого физического шага:

IEnumerator WaitUntilResting(Rigidbody body)
{
    // Каждая проверка выполняется после расчёта физики
    while (body.velocity.sqrMagnitude != 0f)
    {
        yield return new WaitForFixedUpdate();
    }

    Debug.Log("Тело остановилось");
}

WaitForFixedUpdate или Update

Если задача привязана к физике, корутина не обязательна — достаточно FixedUpdate. Корутина нужна, когда после ожидания должна выполниться последовательность дальнейших действий:

Подход Когда использовать
FixedUpdate Одно и то же действие каждый физический шаг: сила, проверка коллизий, перемещение Rigidbody
yield return new WaitForFixedUpdate() Цепочка: подождать физику → сделать что-то → подождать снова, с логикой между шагами

WaitForFixedUpdate и WaitForEndOfFrame

Несмотря на созвучие, инструкции ждут разного:

Инструкция Момент возобновления
WaitForFixedUpdate После физического обновления, до кадрового рендера
WaitForEndOfFrame В конце кадра, после отрисовки всех камер и интерфейса

Скриншот через ReadPixels требует WaitForEndOfFrame — физика тут ни при чём. Нахождение Rigidbody после шага симуляции — WaitForFixedUpdate. Подробнее про второй вариант в статье WaitForEndOfFrame в Unity C#.

Частые ошибки

1. Ожидание как замена FixedUpdate

Если внутри цикла нет ни yield return, ни логики, зависящей от порядка шагов, проще писать в FixedUpdate. Корутина ради корутины добавляет аллокации.

2. Ожидание в Update

yield допустим только в методе с типом IEnumerator. В Update WaitForFixedUpdate не напишется — ошибка компиляции.

3. Забытый FixedUpdateDeltaTime

Если в проекте Time.fixedDeltaTime изменён (например, 0.05 для экономии), WaitForFixedUpdate будет ждать дольше, чем кажется. Причина не в инструкции, а в настройке физики.

4. Зависание при timeScale = 0

Фиксированные обновления зависят от игрового времени. При остановленном времени (Time.timeScale = 0) физика не обновляется, и корутина ждёт бесконечно. Для пауз используйте WaitForSecondsRealtime.

Связанные статьи