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.