Вы открываете браузерную игру «на минутку» — и обнаруживаете себя за ней через час. Это не случайность и не магия. За этим стоит конкретная конструкция: игровой цикл. Разберём как он устроен с двух сторон — со стороны дизайна и со стороны кода.
Три петли, которые держат игрока
Любая инкрементальная игра работает одновременно на трёх уровнях времени.
Микро-петля — до 5 минут. Купил юнита, отбил волну, получил золото, купил ещё. Немедленная награда. Игрок видит результат каждого действия почти мгновенно.
Макро-петля — от получаса до часа. Накопил на нового типа юнита, открыл ветку навыков, добрался до босса. Цели чуть дальше, но всё ещё достижимы за сессию.
Мета-петля — дни и недели. Система Prestige: сбрасываешь прогресс в обмен на постоянный бонус. Следующее прохождение сильнее. Возвращаешься снова.
Если в игре есть только микро-петля — она надоедает за час. Только мета — игрок не понимает зачем заходить сегодня. Все три вместе создают ощущение, что всегда есть куда двигаться.
Именно поэтому при открытии игры всегда виден и текущий бой, и прогресс-бар к следующему навыку, и счётчик Prestige-очков. Три горизонта одновременно.
Как это устроено в коде
Технически весь игровой процесс держится на одной функции — requestAnimationFrame. Браузер вызывает её примерно 60 раз в секунду, и каждый такой вызов — один кадр игры.
Главное что нужно считать внутри кадра — delta time: сколько миллисекунд прошло с прошлого вызова. Без этого игра будет работать быстрее на мощных машинах и медленнее на слабых.
На каждом кадре выполняется цепочка систем:
- WaveManager — двигает врагов, управляет спавном
- CombatSystem — считает урон, начисляет ресурсы
- ResourceManager — обновляет пассивный доход
- Renderer — рисует всё на Canvas
Почему Canvas, а не React?
Игровая сцена — это сотни объектов, которые обновляются 60 раз в секунду. React не рассчитан на такую частоту обновлений: каждый setState запускает цикл согласования, и на сложных сценах это даёт заметные просадки.
Решение: Canvas рисует игру, React рисует интерфейс поверх неё. Магазин юнитов, HUD с ресурсами, дерево навыков — всё это обычные React-компоненты, которые лежат поверх Canvas через position: absolute. Они обновляются редко и реагируют на действия игрока. Canvas работает в своём ритме и не знает о React ничего.
Два слоя не мешают друг другу. Canvas получает свои 60fps, React — свой привычный жизненный цикл.
Защита от заморозки
Когда игрок переключает вкладку, браузер приостанавливает requestAnimationFrame. При возврате delta time может оказаться огромной — несколько минут или часов.
Для этого в игре стоит кап: delta не может превышать 100 миллисекунд за кадр. Всё что сверху — обрабатывается отдельно как оффлайн-прогресс. Так игра не «взрывается» после долгого отсутствия.
Что в итоге
Петли и game loop — не отдельные механики. Это одна и та же идея на двух уровнях: дизайнер строит петли чтобы игрок всегда чувствовал движение вперёд, разработчик строит game loop чтобы это движение работало стабильно на любом устройстве.
В следующей статье разберём волны и врагов: как скалирование создаёт напряжение — и почему число 0.12 в формуле HP появилось не случайно.
#gamedev, #инди-игры, #разработка игр, #javascript, #typescript