← Все материалы
Основы

Игровой цикл и время

Отделяем скорость игры от частоты кадров и проверяем поведение при паузах и просадках FPS.

Что нужно знать

Достаточно понимать переменные, функции и координаты. После урока вы сможете объяснить, кто обновляет мир, в каких единицах задана скорость и почему один нарисованный кадр не всегда равен одному шагу симуляции.

Принцип

Игровой цикл связывает ввод, изменение состояния и отображение результата. Даже если персонаж стоит, мир может продолжать жить. В готовом движке этот цикл уже существует: ваша задача подключить логику к подходящим точкам обновления. Общая модель разобрана в главе Game Loop.

Запись x += 3 означает три единицы за вызов. При разной частоте вызовов персонаж проходит разное расстояние. Запись x += speed * dt задаёт движение через прошедшее время, где speed измеряется в единицах в секунду, а dt в секундах. Но это само по себе не делает любую физическую модель устойчивой.

Для чувствительной к шагу симуляции используют фиксированный интервал обновления. Накопленное реальное время расходуется на несколько одинаковых шагов; отрисовка может происходить реже или чаще. Ограничение числа догоняющих шагов требует явного решения: замедлять игровой мир или иначе обрабатывать перегрузку. Подробности есть в Fix Your Timestep!.

Практический пример

Начните с одного квадрата без столкновений. Пусть он движется со скоростью 120 единиц в секунду. За две секунды непрерывного движения ожидается 240 единиц независимо от количества показанных кадров.

update(dtSeconds, input):
    if input.moveRight:
        player.x += 120 * dtSeconds

render():
    drawSquare(player.x, player.y)

Это учебный псевдокод, а не готовый цикл движка. Сначала вызовите update 120 раз с шагом 1/60, затем 60 раз с шагом 1/30. Сравните координаты с небольшой допустимой погрешностью. Отдельно попробуйте меняющиеся интервалы с той же суммой времени.

В браузере requestAnimationFrame вызывает функцию перед перерисовкой и обычно приостанавливается в фоновых вкладках. Его временная метка задана в миллисекундах. Эти условия документирует MDN. При возвращении из фона задайте политику возобновления: например, сбросить предыдущую метку времени и продолжить с паузы.

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

Не обновляйте координаты одновременно в физическом и визуальном callback (обработчике, который вызывает движок). Не применяйте dt дважды, если метод движка уже ожидает скорость. Не запускайте второй цикл при каждом рестарте сцены. Не используйте скрытие вкладки как неявную паузу: звук, сетевые события и таймеры могут иметь другой жизненный цикл.

Сначала проверяйте движение без интерполяции. Когда логика верна, визуальное сглаживание можно добавить отдельно. Иначе плавная картинка способна скрыть ошибку в скорости или порядке обновления.

Проверка результата

  • За одинаковое игровое время квадрат проходит одинаковый путь при разных частотах обновления.
  • Пауза останавливает нужные системы, а интерфейс остаётся доступным.
  • Возврат из фоновой вкладки не телепортирует персонажа.
  • Повторный запуск сцены не удваивает скорость.
  • В коде подписаны единицы времени и скорости.

Задание для ИИ-агента

Найди владельца игрового цикла и все места изменения координат игрока. Опиши единицы скорости и времени. Предложи минимальное исправление зависимости движения от FPS и проверь путь за две секунды при нескольких последовательностях dt. Отдельно укажи поведение паузы и восстановления фокуса. Не меняй физический backend без доказанной необходимости.

Источники и дальнейшее чтение

CC BY 4.0 · Game Dev DB contributors