{"schemaVersion":1,"record":{"key":"lesson/game-loop","type":"lesson","id":"game-loop","lang":"ru","title":"Игровой цикл и время","summary":"Отделяем скорость игры от частоты кадров и проверяем поведение при паузах и просадках FPS.","updatedAt":"2026-10-20","status":"active","url":"https://gamemechanics.pro/ru/lessons/game-loop/","jsonUrl":"https://gamemechanics.pro/api/v1/knowledge/ru/lesson/game-loop.json","markdownUrl":"https://gamemechanics.pro/content/knowledge/ru/lesson/game-loop.md","aliases":[],"tags":["game-loop","time","simulation"],"sourceIds":["source-4794cbcf5206cc3d","source-61d9bfebd277d850","source-e55fb78e3762cd48"],"sources":[{"id":"source-4794cbcf5206cc3d","title":"Robert Nystrom: Game Loop","url":"https://gameprogrammingpatterns.com/game-loop.html","verifiedAt":null},{"id":"source-61d9bfebd277d850","title":"MDN: requestAnimationFrame","url":"https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame","verifiedAt":null},{"id":"source-e55fb78e3762cd48","title":"Glenn Fiedler: Fix Your Timestep!","url":"https://github.com/mas-bandwidth/gafferongames/blob/main/content/post/fix_your_timestep.md","verifiedAt":null}],"sections":[{"id":"prerequisites","title":"Что нужно знать","body":"Достаточно понимать переменные, функции и координаты. После урока вы сможете объяснить, кто обновляет мир, в каких единицах задана скорость и почему один нарисованный кадр не всегда равен одному шагу симуляции."},{"id":"principle","title":"Принцип","body":"Игровой цикл связывает ввод, изменение состояния и отображение результата. Даже если персонаж стоит, мир может продолжать жить. В готовом движке этот цикл уже существует: ваша задача подключить логику к подходящим точкам обновления. Общая модель разобрана в [главе Game Loop](https://gameprogrammingpatterns.com/game-loop.html).\n\nЗапись `x += 3` означает три единицы за вызов. При разной частоте вызовов персонаж проходит разное расстояние. Запись `x += speed * dt` задаёт движение через прошедшее время, где `speed` измеряется в единицах в секунду, а `dt` в секундах. Но это само по себе не делает любую физическую модель устойчивой.\n\nДля чувствительной к шагу симуляции используют фиксированный интервал обновления. Накопленное реальное время расходуется на несколько одинаковых шагов; отрисовка может происходить реже или чаще. Ограничение числа догоняющих шагов требует явного решения: замедлять игровой мир или иначе обрабатывать перегрузку. Подробности есть в [Fix Your Timestep!](https://github.com/mas-bandwidth/gafferongames/blob/main/content/post/fix_your_timestep.md)."},{"id":"example","title":"Практический пример","body":"Начните с одного квадрата без столкновений. Пусть он движется со скоростью 120 единиц в секунду. За две секунды непрерывного движения ожидается 240 единиц независимо от количества показанных кадров.\n\n```text\nupdate(dtSeconds, input):\n    if input.moveRight:\n        player.x += 120 * dtSeconds\n\nrender():\n    drawSquare(player.x, player.y)\n```\n\nЭто учебный псевдокод, а не готовый цикл движка. Сначала вызовите `update` 120 раз с шагом `1/60`, затем 60 раз с шагом `1/30`. Сравните координаты с небольшой допустимой погрешностью. Отдельно попробуйте меняющиеся интервалы с той же суммой времени.\n\nВ браузере `requestAnimationFrame` вызывает функцию перед перерисовкой и обычно приостанавливается в фоновых вкладках. Его временная метка задана в миллисекундах. Эти условия документирует [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame). При возвращении из фона задайте политику возобновления: например, сбросить предыдущую метку времени и продолжить с паузы."},{"id":"mistakes","title":"Частые ошибки","body":"Не обновляйте координаты одновременно в физическом и визуальном callback (обработчике, который вызывает движок). Не применяйте `dt` дважды, если метод движка уже ожидает скорость. Не запускайте второй цикл при каждом рестарте сцены. Не используйте скрытие вкладки как неявную паузу: звук, сетевые события и таймеры могут иметь другой жизненный цикл.\n\nСначала проверяйте движение без интерполяции. Когда логика верна, визуальное сглаживание можно добавить отдельно. Иначе плавная картинка способна скрыть ошибку в скорости или порядке обновления."},{"id":"checks","title":"Проверка результата","body":"- За одинаковое игровое время квадрат проходит одинаковый путь при разных частотах обновления.\n- Пауза останавливает нужные системы, а интерфейс остаётся доступным.\n- Возврат из фоновой вкладки не телепортирует персонажа.\n- Повторный запуск сцены не удваивает скорость.\n- В коде подписаны единицы времени и скорости."},{"id":"agent-task","title":"Задание для ИИ-агента","body":"> Найди владельца игрового цикла и все места изменения координат игрока. Опиши единицы скорости и времени. Предложи минимальное исправление зависимости движения от FPS и проверь путь за две секунды при нескольких последовательностях dt. Отдельно укажи поведение паузы и восстановления фокуса. Не меняй физический backend без доказанной необходимости."}],"relations":[{"type":"requires","direction":"outgoing","label":"Перед началом","key":"lesson/scope-and-prototype","title":"Скоуп и проверяемый прототип","url":"https://gamemechanics.pro/ru/lessons/scope-and-prototype/","jsonUrl":"https://gamemechanics.pro/api/v1/knowledge/ru/lesson/scope-and-prototype.json","markdownUrl":"https://gamemechanics.pro/content/knowledge/ru/lesson/scope-and-prototype.md"},{"type":"requires","direction":"incoming","label":"Продолжить изучение","key":"lesson/input-and-feedback","title":"Ввод и обратная связь","url":"https://gamemechanics.pro/ru/lessons/input-and-feedback/","jsonUrl":"https://gamemechanics.pro/api/v1/knowledge/ru/lesson/input-and-feedback.json","markdownUrl":"https://gamemechanics.pro/content/knowledge/ru/lesson/input-and-feedback.md"},{"type":"requires","direction":"incoming","label":"Продолжить изучение","key":"lesson/collision-basics","title":"Основы столкновений","url":"https://gamemechanics.pro/ru/lessons/collision-basics/","jsonUrl":"https://gamemechanics.pro/api/v1/knowledge/ru/lesson/collision-basics.json","markdownUrl":"https://gamemechanics.pro/content/knowledge/ru/lesson/collision-basics.md"},{"type":"requires","direction":"incoming","label":"Продолжить изучение","key":"lesson/performance-budget","title":"Бюджет производительности","url":"https://gamemechanics.pro/ru/lessons/performance-budget/","jsonUrl":"https://gamemechanics.pro/api/v1/knowledge/ru/lesson/performance-budget.json","markdownUrl":"https://gamemechanics.pro/content/knowledge/ru/lesson/performance-budget.md"},{"type":"contains","direction":"incoming","label":"Учебные маршруты","key":"path/first-prototype","title":"Первый проверяемый прототип","url":"https://gamemechanics.pro/ru/learn/first-prototype/","jsonUrl":"https://gamemechanics.pro/api/v1/knowledge/ru/path/first-prototype.json","markdownUrl":"https://gamemechanics.pro/content/knowledge/ru/path/first-prototype.md"},{"type":"requires","direction":"incoming","label":"Продолжить изучение","key":"path/npc-behavior","title":"Поведение NPC","url":"https://gamemechanics.pro/ru/learn/npc-behavior/","jsonUrl":"https://gamemechanics.pro/api/v1/knowledge/ru/path/npc-behavior.json","markdownUrl":"https://gamemechanics.pro/content/knowledge/ru/path/npc-behavior.md"},{"type":"uses","direction":"outgoing","label":"Источники в подборке","key":"resource/game-loop-pattern","title":"Игровой цикл: Game Programming Patterns","url":"https://gamemechanics.pro/ru/resources/#resource-game-loop-pattern","jsonUrl":"https://gamemechanics.pro/api/v1/knowledge/ru/resource/game-loop-pattern.json","markdownUrl":"https://gamemechanics.pro/content/knowledge/ru/resource/game-loop-pattern.md"},{"type":"uses","direction":"outgoing","label":"Источники в подборке","key":"resource/animation-frame","title":"requestAnimationFrame на MDN","url":"https://gamemechanics.pro/ru/resources/#resource-animation-frame","jsonUrl":"https://gamemechanics.pro/api/v1/knowledge/ru/resource/animation-frame.json","markdownUrl":"https://gamemechanics.pro/content/knowledge/ru/resource/animation-frame.md"},{"type":"uses","direction":"outgoing","label":"Источники в подборке","key":"resource/fixed-timestep","title":"Fix Your Timestep!","url":"https://gamemechanics.pro/ru/resources/#resource-fixed-timestep","jsonUrl":"https://gamemechanics.pro/api/v1/knowledge/ru/resource/fixed-timestep.json","markdownUrl":"https://gamemechanics.pro/content/knowledge/ru/resource/fixed-timestep.md"}],"placements":[],"supersededBy":[],"attributes":{"category":"foundations","level":"beginner","minutes":6},"endsAt":null,"provenance":"Авторский материал gamemechanics.pro. Учебные примеры требуют проверки в своём проекте.","license":"CC-BY-4.0","codeLicense":"MIT","attribution":"gamemechanics.pro contributors"}}