Разработка игр

Введение в разработку игр

60 FPS - это 16.6 мс на кадр. За эти 16.6 мс движок должен обработать ввод, обновить физику всех объектов сцены, пересчитать AI, анимации, LOD, frustum culling - и передать команды рендеру. В AAA-игре это 10 000+ объектов. Red Dead Redemption 2 рендерит 1 миллион объектов травы в одном кадре. Бюджет - 16.6 мс, превысить нельзя. Добро пожаловать в разработку игр.

  • **Game loop (requestAnimationFrame):** тот же паттерн в браузере - React Three Fiber, Three.js, WebGL-визуализации; 60 FPS = 16.6 мс бюджет на кадр
  • **ECS в Unity DOTS и Overwatch**: Blizzard перешёл на ECS для серверной симуляции 60 Hz с тысячами объектов; Unity DOTS даёт x10-100 производительность против MonoBehaviour
  • **LOD (Level of Detail) в AAA**: персонаж вблизи - 100K полигонов, вдали - 100; автоматическое переключение по дистанции; Red Dead Redemption 2, Cyberpunk 2077
  • **Procedural generation в Minecraft и No Man's Sky**: seed → детерминированный мир; весь бесконечный мир Minecraft генерируется на лету из числа

Game Loop

60 FPS = 16.6 мс на кадр. 30 FPS = 33 мс. Это не предпочтение - это жёсткий бюджет. За каждые 16.6 мс движок обрабатывает ввод, обновляет физику, AI, анимации и рисует сцену. Каждая игра - от Pong до Cyberpunk - работает по одному принципу: бесконечный цикл **game loop**, бьющийся как сердце игры.

Проблема наивного цикла: на мощном компьютере мяч летит быстрее, чем на слабом - update() вызывается чаще. В 1999 году Quake III Arena работал именно так и был привязан к FPS. Решение - **fixed timestep**: физика обновляется с фиксированным шагом (60 Hz = 16.6 мс), рендеринг - как можно чаще.

**Fixed timestep** гарантирует детерминизм: одинаковый ввод даёт одинаковый результат на любом оборудовании. Это критично для онлайн-игр (lockstep networking), воспроизведения реплеев и стабильности физики.

Параметр alpha в render(alpha) - **интерполяция**: физика 60 Hz, монитор 144 Hz. Между «физическими» кадрами рисуются промежуточные положения объектов. Результат: плавная картинка при стабильной физике. Именно так Cyberpunk 2077 выглядит плавно на 144 Hz мониторе, не разгоняя физический симулятор.

Зачем в game loop используется fixed timestep?

Entity-Component-System

Как организовать код игры? Наследование (Enemy extends Character extends GameObject) быстро превращается в nightmare: diamond problem, ромбовидное наследование, взрыв иерархии. Blizzard столкнулся с этим в Overwatch - 40+ типов героев, каждый с уникальными способностями. Решение: **Entity-Component-System (ECS)**, где данные отделены от логики.

**Entity** - просто идентификатор (число). **Component** - контейнер данных без поведения: Position {x, y}, Health {current, max}, Sprite {texture, frame}. **System** - функция, которая обрабатывает все entity с определённым набором компонентов.

Преимущества ECS: **композиция вместо наследования** (любая комбинация компонентов), **cache-friendly** (данные хранятся в массивах, а не разбросаны по куче), **параллелизм** (системы, работающие с разными компонентами, можно запускать параллельно).

ECS используют Unity DOTS (x10-100 производительность против MonoBehaviour), Unreal Mass Entity, Bevy (Rust), Flecs (C/C++). Overwatch (Blizzard) перешёл на ECS для 60 Hz серверной симуляции с тысячами объектов. Даже в классическом Unity MonoBehaviour - это уже компонент на GameObject (entity): частичный ECS без осознания.

В архитектуре ECS, что содержит логику поведения?

Управление сценами

Игра состоит из экранов: главное меню, игровой уровень, пауза, магазин, экран поражения. Каждый из них - **сцена** (scene). В AAA-игре переход между уровнями - это 2-5 GB текстур и моделей. Загрузить всё сразу нельзя. Scene manager управляет загрузкой и выгрузкой ресурсов - именно в этом его главная задача, а не только переходы.

Сцены бывают **стековые** и **заменяющие**. Push-сцена (пауза) накладывается поверх предыдущей - при снятии паузы игра продолжается. Replace-сцена (переход меню → игра) полностью заменяет текущую - меню выгружается из памяти.

Загрузка ресурсов (текстуры, звуки, модели) - самая медленная часть перехода. Решения: **loading screen** (асинхронная загрузка с прогресс-баром), **предзагрузка** (загружать следующий уровень во время текущего), **пулинг** (переиспользование объектов вместо создания/уничтожения).

Жизненный цикл сцены: **onEnter()** - загрузка ресурсов и инициализация, **update(dt)** + **render()** - основной цикл, **onPause()** / **onResume()** - при push/pop другой сцены, **onExit()** - освобождение ресурсов. Пропустить onExit - утечка памяти. В Cyberpunk 2077 при первом релизе были именно такие утечки на PS4, приводившие к crash.

Чем push-сцена отличается от replace-сцены?

Обработка ввода

Игрок взаимодействует с игрой через устройства ввода: клавиатура, мышь, геймпад, тачскрин. Задача системы ввода - абстрагировать конкретные устройства в **действия** (actions), чтобы игровая логика не зависела от оборудования.

Различают три типа событий ввода: **pressed** (кнопка только что нажата - один кадр), **held** (кнопка удерживается), **released** (кнопка отпущена - один кадр). Для движения обычно используют held, для прыжка - pressed, для стрельбы - зависит от оружия.

УстройствоТип вводаОсобенности
КлавиатураДискретный (0/1)Множество одновременных клавиш, нет аналоговых осей
МышьПозиция + кнопкиСубпиксельная точность, delta movement, скролл
ГеймпадСтики + кнопки + триггерыАналоговые оси (-1..1), dead zone, вибрация
ТачскринМультитач + жестыНет hover, виртуальные кнопки перекрывают экран

**Dead zone** - область вокруг центра аналогового стика, где ввод игнорируется. Без dead zone персонаж «дрейфует» из-за неточности стика. Типичное значение: 0.15-0.25 от полного отклонения.

Ввод обрабатывается в начале game loop, до update(). Все события за кадр собираются в буфер и обрабатываются разом - физика получает консистентный snapshot. В сетевых играх (Valorant, CS2) input lag измеряется в единицах: 1 кадр @ 128 Hz сервер = 7.8 мс. Именно поэтому профессионалы играют на 240 Hz мониторах.

Полная картина game loop: processInput() читает устройства и маппит в действия, update(dt) обновляет мир с fixed timestep 16.6 мс, render() рисует результат с интерполяцией. Три шага - фундамент любой игры от Tetris до GTA VI. Бюджет 16.6 мс никуда не девается.

Игровой движок - это и есть игра

Движок - инструмент (рендеринг, физика, ввод, звук). Игра - геймплей, дизайн, контент, нарратив и арт поверх движка.

Fortnite и PUBG оба на Unreal Engine. Ori and the Blind Forest и Temple Run оба на Unity. Движок не определяет жанр и качество. Minecraft написан без AAA-движка (Java → C++) и продался в 238 миллионов копий. Среди самых успешных инди-игр многие используют собственные движки или Godot.

Зачем нужен input mapping?

Ключевые идеи

  • **Game loop** - 16.6 мс на кадр при 60 FPS: input → update (fixed timestep) → render; fixed timestep гарантирует детерминизм физики на любом железе
  • **ECS** разделяет Entity (ID) / Component (данные) / System (логика); Unity DOTS и Overwatch используют ECS для x10-100 производительности против наследования
  • **Scene manager** - стек сцен: push (пауза поверх игры) и replace (меню → уровень); жизненный цикл onEnter/onExit управляет загрузкой ресурсов
  • **Input mapping** абстрагирует устройства в действия - один код работает для клавиатуры, геймпада и тачскрина

Связанные темы

Основы разработки игр связаны со множеством дисциплин:

  • Игровые движки — Unity, Unreal и Godot реализуют game loop, ECS и scene management «из коробки»
  • 2D рендеринг — Функция render() из game loop - целая наука: спрайты, тайлмапы, анимации, параллакс

Вопросы для размышления

  • Почему ECS победил наследование в геймдеве? React использует похожую архитектуру (компоненты без состояния + хуки). Одна и та же идея?
  • Что произойдёт с физикой если убрать fixed timestep: Quake III Arena был привязан к FPS, и на быстрых машинах появлялись баги. Конкретно какие?
  • 16.6 мс бюджет кадра. LOD, frustum culling, draw call batching - расставьте по приоритету что даст максимальный выигрыш в современных AAA-играх.

Связанные уроки

  • cg-01 — Компьютерная графика - основа рендеринга в играх: матрицы трансформаций, шейдеры, z-буфер
  • alg-01-big-o — Алгоритмы критичны в геймдеве: collision detection, pathfinding (A*), spatial partitioning
  • se-01 — Архитектурные паттерны (Entity-Component-System, Observer, State Machine) - те же, что в обычной разработке
  • gd-02 — Понимание движков открывает путь к игровым механикам и физике
  • la-06-transformations
Введение в разработку игр

0

1

Войти