Жизненный цикл: Awake, Start, Update, FixedUpdate

Unity вызывает методы скрипта в строгом порядке. Понять этот порядок — значит перестать удивляться, почему что-то «не инициализировалось».

Суть: у MonoBehaviour есть жизненный цикл. Awake — самая ранняя инициализация. Start — после Awake, перед первым кадром. Update — каждый кадр (логика, ввод). FixedUpdate — фиксированно для физики. LateUpdate — после всех Update (камера).

Движок вызывает методы скрипта не как попало, а в определённом порядке. Каждый метод предназначен для своего этапа жизни объекта. Если положить код не туда — получишь баги вроде «ссылка пустая» или «физика дёргается».

Как работает под капотом

Жизненный цикл MonoBehaviour:

  Awake()      -> объект создан. Настрой СВОИ поля
     |
  OnEnable()   -> объект включён
     |
  Start()      -> перед 1-м кадром. Связи с ДРУГИМИ объектами
     |
  +--> цикл каждый кадр: ----------------------+
  |   FixedUpdate() -> физика (фикс. шаг)      |
  |   Update()      -> логика, ввод            |
  |   LateUpdate()  -> после всех Update       |
  +-------------------------------------------+
     |
  OnDestroy()  -> объект уничтожен

Ключевые различия:

  • Awake — настраивай свои компоненты и переменные. Вызывается до Start у всех объектов.
  • Start — настраивай связи с другими объектами: к моменту Start все Awake уже отработали, объекты существуют.
  • Update — игровая логика и чтение ввода, каждый кадр. Частота зависит от FPS.
  • FixedUpdate — физика. Вызывается с фиксированным шагом (по умолчанию 50 раз в секунду), независимо от FPS. Сюда — работа с Rigidbody.
  • LateUpdate — после всех Update. Идеально для камеры, следящей за героем: герой уже сдвинулся в Update, камера догоняет в LateUpdate.

Важнейший момент про время: в Update кадры неравномерны, поэтому движение умножают на Time.deltaTime. В FixedUpdate шаг постоянный — там используют Time.fixedDeltaTime.

Смоделируем порядок вызовов и разную частоту Update/FixedUpdate на Python:

# Имитация: за 1 секунду Update зависит от FPS, FixedUpdate стабилен
fps = 60               # кадров в секунду (меняется!)
fixed_steps = 50       # шагов физики в секунду (стабильно)

print("Awake: настроили свои поля")
print("Start: связались с другими объектами")

update_calls = fps
fixed_calls = fixed_steps
print(f"За 1 сек: Update вызван {update_calls} раз, FixedUpdate {fixed_calls} раз")

# Если FPS просядет до 30 — Update реже, а физика всё та же
fps = 30
print(f"При лагах: Update {fps} раз, FixedUpdate всё равно {fixed_steps} раз")

Та же логика на Python ▶ — видно, почему физику кладут в FixedUpdate: её шаг не зависит от того, тормозит игра или нет. Это делает симуляцию стабильной.

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

  • Двигать Rigidbody в Update. Физику трогают в FixedUpdate, иначе движение рывками и непредсказуемо.
  • Обращаться к другому объекту в Awake. Тот объект может быть ещё не готов. Связи делай в Start.
  • Камера в Update вместо LateUpdate. Камера может «дёргаться», следуя за героем, если двигается раньше него. Слежение — в LateUpdate.
  • Читать ввод в FixedUpdate. Ввод проверяют в Update; в FixedUpdate можно пропустить короткое нажатие.

Best practices

  • Запомни правило: Awake — про себя, Start — про других.
  • Логика и ввод — в Update; физика — в FixedUpdate; камера — в LateUpdate.
  • В Update всегда умножай движение на Time.deltaTime, иначе скорость будет зависеть от FPS.

Итоги: жизненный цикл — это порядок Awake → Start → (FixedUpdate/Update/LateUpdate каждый кадр) → OnDestroy. Awake для своих полей, Start для связей, Update для логики, FixedUpdate для физики, LateUpdate для камеры. Правильное место для кода избавляет от половины багов.

Корутины: код, растянутый во времени

Кроме обычных методов цикла, у MonoBehaviour есть мощный приём — корутины. Это методы, которые умеют «ставить себя на паузу» и продолжаться через несколько кадров или секунд. Звучит сложно, но смысл простой: иногда нужно «подождать 2 секунды, потом взорваться» или «плавно проявить экран за полсекунды». Городить ради этого таймеры в Update неудобно. Корутина пишется почти как обычный код сверху вниз, но в нужных местах говорит движку «продолжи меня позже». Это идеально ложится на жизненный цикл: корутина живёт между кадрами, не блокируя игру. Пока достаточно знать, что такой инструмент есть и решает класс задач «сделай это не сейчас, а через какое-то время» — к нему ты вернёшься, когда понадобятся задержки и плавные эффекты.

Проверьте себя
1. Куда правильно класть работу с физикой (Rigidbody)?
AВ Update
BВ FixedUpdate
CВ Awake
DВ LateUpdate
2. В чём разница между Awake и Start?
AЭто одно и то же
BAwake — настройка своих полей, Start — связи с другими объектами (после всех Awake)
CStart вызывается раньше Awake
DAwake вызывается каждый кадр