Чему мы можем научиться у разработчиков игр

Компьютерные игры поражают меня не только как игрока, но и как программиста.
Первый раз я погрузился в мир геймдева, читая книги «Кровь, код и пиксели» и «Повелители Doom», в которых рассказывалось о непростых буднях создателей игр. Помню, как провёл за чтением все новогодние праздники: истории о создании всем известных хитов были полны малоизвестными пикантными подробностями.

По мере профессионального роста я стал смотреть на игры глазами программиста. Началось всё с видео о скоростном прохождении игр (speed runs), где авторы рассказывали, как, используя знание деталей реализации, киберспортсмены вытворяли в них невообразимые трюки.
Мне стало интересно, как устроены игровые движки, которые позволяют создавать сложные игровые механики и выдавать красивую картинку. Поэтому этим летом я прочитал несколько книг на эту тему и хочу поделиться полученными инсайтами, особенно с теми разработчиками, которые игры не пишут. Эта литература взбодрила меня в море рутинных бэкендерских задач. Надеюсь, и вы сможете взглянуть на привычные вещи под новым углом.
Game Programming Patterns. Robert Nystrom
Одной из первых мне попалась книга бывшего разработчика из Electronic Arts. Автор разобрал применение паттернов из «Банды четырех» на примерах создания игровых механик и предложил несколько собственных идей.
Если вам интересно в ненапряжной форме узнать, как устроен главный цикл игры, как достигается плавность отрисовки и корректность симуляции, – это тоже есть в книге.

Всё и сразу
В книге «Game Engine Architecture» компьютерная игра была определена как «интерактивная агентская компьютерная симуляция в режиме реального времени». Из этого следует, что особенность игр – большой объём вычислений, которые нужно выполнять максимально быстро. Пользователь будет пользоваться даже медленным сайтом, если получает важную услугу. Но в лагающую игру играть никто не станет.
Но на этом сложности не заканчиваются. Мало сделать игру приятной глазу – нужно предложить игроку механники, с которыми ему будет интересно взаимодействовать. Креативу геймдизайнеров нет предела, поэтому разработчикам приходится находить способы создания очень гибких архитектур, которые позволят быстро экспериментировать с разными элементами игры.
ECS
Когда я начал искать статьи об архитектуре игр, первые же ссылки содержали неизвестную мне аббревиатуру ECS. ECS расшифровывается как Entity-Component-System. Это подход к дизайну игровых сущностей, который сочетает в себе недоступную для ООП динамическую гибкость и производительность, не достижимую при динамической диспетчеризации полиморфных методов.
Вместо того чтобы создавать статичные иерархии наследования объектов, этот подход предлагает разобрать монолитный объект игровой сущности с множеством полей и методов на независимые множества отдельных механик. Они связываются с помощью общего для всех компонентов объекта уникального идентификатора.

В итоге мы получаем возможность не только в рантайме радикально менять свойства объектов (например, научить машину летать, если игрок подобрал ракетный двигатель), но и максимально быстро обрабатывать состояния однотипных компонентов благодаря лучшей локальности данных для кэша процессора. Win-Win.
О деталях можно почитать подробнее или за 7 минут посмотреть в этом видео:
Разобравшись в этой идее, я был впечатлён настолько нестандартным взглядом игроделов на, казалось бы, известную проблему. Оказалось, вот до чего можно додуматься, когда нужны бескомпромиссные гибкость и производительность.
Монолиты, которые смогли
Больше всего меня впечатлила книга Джейсона Грегори «Game Engine Architecture», который сейчас разрабатывает игровой движок для игр Naughty Dog (Uncharted, The Last of Us). В ней автор буквально «до винтиков» разбирает устройство современных игровых движков: от верхнеуровнего описания всех подсистем до математики расчёта столкновений. Как движки создают визуальные и звуковые эффекты, как работают анимации и создаются «кирпичики» для геймплея – всё это описано в деталях.
Когда задумываешься о том, как устроены современные игры, интуиция подсказывает, что там уж точно «всё связано со всем». И как в таких условиях код не превращается в один сплошной «комок грязи» – это просто чудо.
На самом деле движки хорошо структурированы. В них огромное количество подсистем, и многие из них решают какую-то одну задачу для всех объектов игрового мира. Но чтобы игра не разваливалась на части, некоторые подсистемы управляют другими. Например, подсистема физики передаёт данные подсистеме анимаций. Это хороший пример того, насколько полезно отойти подальше от мелких деталей и посмотреть на всё происходящее с «большой высоты», чтобы понять суть запрограммированного и тем самым управлять сложностью создаваемой программы.
Ещё один интересный трюк, который используется в движках повсеместно, – не реализация сложных алгоритмов «в лоб», а максимальное упрощение вещей до той степени, пока разница не станет хоть как-то заметна пользователю. Показательный пример – всё, что связано с обработкой персонажей в игровом мире. Никто не пытается симулировать движение и отрисовку каждой точки игрового объекта: придуманы реалистичные упрощённые модели, которые всё ещё кажутся игрокам настоящими, но которые обходятся процессору существенно дешевле.
Последний момент, который хочется подчеркнуть: сложная бизнес-логика – это не приговор для прозводительности программы. Разработчики игр активно используют знание нюансов работы памяти и процессора, чтобы эффективно обсчитывать огромное количество объектов. Стоит пересмотреть структуру хранения ваших объектов в памяти, сделать их CPU cache-friendly, и скорость обработки увеличится в разы.

«Talk is cheap. Show me the code!»
Не так много игр, чьи исходники выложены в интернете. Особняком здесь стоит конечно же Doom от «Id Software».
Лирику о том, как Джон Кармак несколько раз устраивал революции в играх, можно почитать в «Повелителях Doom». Но если вам хочется нырнуть в технические детали с головой, то лучше сделать это вместе с книгой «Game Engine Black Book. Doom», где есть пояснения к нетривиальному коду игры на C.
Движок Doom во многом опередил своё время. Он выделялся не только революционной графикой. Внутри он был спроектирован как кроссплатформенный, поддерживал расширения через моды, умел проигрывать демки, имел инструменты для создания уровней и скриптов.

В общем, геймдев, как никто другой олицетворяет известную фразу: «Жить захочешь – ещё и не так раскорячишься» 😄 Приятного чтения!