Итак, вы хотите сделать игровой движок
- title
- Итак, вы хотите сделать игровой движок
- type
- summary
- summary
- lisyarus о том, что такое игровой движок, из чего он состоит и почему начинать стоит с разработки игр
- tags
- gamedev, game-engines, c++, software-design
- created
- 2026-07-29
- updated
- 2026-07-29
- lang
- ru
- translation_of
- so-you-want-to-make-a-game-engine
- source_updated
- 2026-07-29
- translated
- 2026-09-01
- translator
- lllm/antigravity/gemini-3.7-flash-medium
Написано в сентябре 2023 года в разгар скандала с ценовой политикой Unity, когда споры шли между позициями "немедленно сменить движок" и "переход займёт месяцы или годы", а третьим лагерем было "написать свой". Позиция lisyarus заключается в том, что все три варианта верны в зависимости от ситуации, а более глубокий и интересный вопрос - что вообще значит "сделать игровой движок".
Его собственный опыт нетипичен: он никогда не работал ни с какими движками, кроме своего собственного, psemek. Он развивал его три года примерно до 100 тысяч строк кода, выпустил на нём десяток джем-игр и один коммерческий проект (Costa Verde Transport Department). Дистрибутив весит меньше 2 МБ, и почти весь этот объём приходится на SDL2.dll.
Определение намеренно бесполезно
Он перебирает очевидные варианты и отбрасывает их. SDL2 - библиотека абстракции над платформой, OpenGL - низкоуровневый графический API, stb_image - загрузчик изображений; ничто из этого не движок. Но крошечный проект, объединяющий все три компонента для перемещения спрайтов по окну, звучит именно как движок. Графика не может служить критерием, поскольку текстовые игры в терминале - это тоже игры. Пользовательский ввод тоже не критерий, ведь калькулятор игрой не является.
Вывод, к которому он приходит: игровой движок - это любой набор инструментов, библиотек и прочих вещей, предназначенный для помощи в создании игр. Формулировка расплывчата до бесполезности, и именно в этом суть. Ретро-платформеру с двухмерным пиксель-артом нужны ввод, вывод картинок на экран и немного звука - от двух до тридцати дней работы в зависимости от опыта и упрямства, что составляет около 1% проекта. Остальные 99% - это создание самой игры. Движок может быть как отдельным специализированным файлом на 100 строк, так и промышленным монстром на 100 ГБ, и маленькие решения - всё ещё движки.
Причины писать свой движок в основном не технические. Уход от корпоративных решений, полный контроль над стеком, компактные бинарники, использование любимого языка и два главных для автора фактора - возможность многому научиться и удовольствие от процесса. Отдельно он выделяет отрезвляющий эффект раздражения: претензии к чужим движкам обычно вызваны несовпадением чужих приоритетов с вашими. Когда движок ваш собственный, у отсутствующей возможности есть очевидная причина: вы её не написали, потому что были заняты. Винить себя за свою работу куда сложнее, чем других за их решения.
Причины не писать движок вполне ожидаемы: это трудно, долго, вам никогда не превзойти Unreal, в вашем движке годами не будет хватать возможностей, и можно выгореть, занимаясь движком вместо самой игры. Затраты времени можно хотя бы амортизировать между несколькими проектами, если заложить это заранее.
Разбор составляющих
Список ожиданий от "достаточно крупного" движка огромен: программирование, окно, игровой цикл, ввод, графика, звук, ассеты, физика, скрипты, сеть, ИИ, интерфейс, архитектура, инструментарий, дистрибуция. Большинство движков реализует лишь часть этого списка и всё равно остаётся движками. Составляющие, по которым у автора есть чёткое мнение:
Программирование. Визуальное программирование - реальный вариант, появившийся задолго до нас, но его поддержка требует создания специализированных инструментов, что заметно усложняет движок. Скриптинг на языке, отличном от языка движка, возможен, но если вы всё равно пишете движок сами, нативный API проще. Выбор языка значения не имеет: сам автор использует C++, поскольку пишет на нём пятнадцать лет, но советует с одинаковой серьёзностью рассматривать Rust, C#, Python, Haskell или Java. Предпочтительный для него формат движка - библиотека для программирования: зависимость, которую вы подключаете в проект игры, а не фреймворк, внутри которого вы заперты.
Окно и игровой цикл. Платформенные API вроде CreateWindowEx неудобны, недружелюбны к новичкам и требуют переписывания под каждую платформу - именно эту рутину берут на себя SDL2, glfw и sfml. Цикл - опрос ввода, симуляция, отрисовка - добавляет измерение времени. То, как он устроен снаружи (явный while (engine::isRunning()), скрытый engine::run() или наследование от application), - вопрос вкуса. Единственная проектная развилка с реальным компромиссом - сколько событий движок обрабатывает сам: чем больше он берёт на себя, тем проще им пользоваться, но иногда пользователю нужно переопределить поведение. Например, не закрывать приложение при запросе на закрытие окна - жестко зашивать такое поведение автор называет смертным грехом.
Графика. Единого правильного уровня абстракции не существует. Двухмерному спрайтовому движку требуется drawSprite(image, x, y) с масштабированием, поворотом и цветокоррекцией, поверх прямого доступа к пикселям, cairo или OpenGL. Трёхмерному движку нужны меши, аффинные преобразования, освещение и скелетная анимация, что на практике требует OpenGL, Vulkan, Direct3D или WebGPU под капотом. psemek не делает ни того, ни другого: он предоставляет обёртку над OpenGL 3.3 (а теперь и WebGPU) без движка рендеринга в привычном понимании, поэтому рендерер переписывается под каждый проект, сохраняя свободу для экспериментов. По обоим этим API есть учебные материалы, разобранные здесь: learn-opengl для core-profile OpenGL и learn-webgpu-cpp для работы с WebGPU на C++. Текст - самое сложное место: здесь нужны FreeType или harfbuzz, а совет автора по поводу написания собственного загрузчика шрифтов прост: не делайте этого. Фиксированный растровый шрифт ASCII работает ровно до тех пор, пока дело не доходит до локализации.
Звук. Библиотеки предоставляют callback, вызываемый многократно в секунду в отдельном потоке и ожидающий сэмплы в определённом формате. Всё, что выше этого, - ваша забота: раздельные потоки для музыки, эффектов и голоса, регулировка громкости для каждого потока и - автор подчёркивает это особо - компрессор на финальном выводе, иначе при наложении нескольких звуков неизбежно появятся щелчки и шум. В psemek есть собственная библиотека микширования, построенная вокруг абстрактных потоков, которые комбинируются в новые потоки и в конце подключаются к единому выходному потоку.
Ассеты. Три варианта с разными проблемами. Вкомпилированные в исполняемый файл: код загрузки вообще не нужен, но изменение ресурса требует перекомпиляции, а моддинг практически невозможен. Отдельные файлы рядом с бинарником с менеджером ассетов, отвечающим за кэширование и предобработку: удобнее всего при разработке и лучше всего для моддинга. Архив (ZIP или собственный формат) - путь, который выбирает большинство крупных движков.
Физика. В большинстве игр её нет вовсе. Её нет в Civilization 6, нет в градостроительных симуляторах. Платформерам и top-down RPG нужны движение персонажа и базовая обработка столкновений - это куда проще написать и значительно проще настроить, чем полноценный физический движок. Проблема слишком большого числа коллизий - это задача пространственного хеширования или квадродеревьев, а не физического движка. Настоящая физика означает подключение Box2D или Bullet.
Скрипты, сеть, ИИ, интерфейс. Скриптинг окупает свою сложность за счёт продуктивности (он приводит слова Дэвида Фрэмптона из Sapiens о том, что Lua поднял его продуктивность на порядки), изоляции, горячей перезагрузки и моддинга. О сетевом коде он судить отказывается, так как никогда не выпускал мультиплеерных игр, ограничиваясь упоминанием Asio и GameNetworkingSockets от Valve. ИИ - это просто код, поэтому любой движок с поддержкой кода поддерживает и ИИ; максимум, что может понадобиться, - специфический паттерн вроде деревьев поведения (behavior trees). Пользовательский интерфейс, по его мнению, нужен практически любой игре и при этом крайне трудно поддаётся абстрагированию: он существует в координатах окна, а не игрового мира, и требует специфического поведения (наведение, клик, перетаскивание), которого нет у игровых сущностей. Автор замечает, что реализация UI заново под каждый проект может быть выгоднее попыток его обобщить.
Архитектура и инструменты. У psemek нет архитектуры: это набор библиотек, спроектированных максимально изолированными друг от друга, из которых берутся только нужные. Монолитная архитектура с базовым классом Object и глобальным менеджером оправдывает себя в больших движках с интроспекцией и инструментами редактирования, но вряд ли полезна для небольших проектов. По поводу инструментов: не пытайтесь построить ещё один Unity, поскольку редактор требует примерно столько же работы, сколько и сам движок. Небольшие специализированные утилиты себя оправдывают: единственным его инструментом был Python-скрипт для конвертации файлов Blender в собственный формат мешей, от которого он отказался при переходе на glTF.
Дистрибуция, которую автор называет самой важной вещью для автоматизации в движке. Программы не запускаются на случайных компьютерах сами по себе. Нативным бинарникам нужны динамические библиотеки в комплекте (для него это SDL2.dll, libpng.dll и ещё несколько), и даже им требуется загрузчик - вроде /lib64/ld-linux-x86-64.so.2 в Linux. Языкам с байт-кодом требуется наличие виртуальной машины, интерпретируемым - сам интерпретатор. Поддержка нескольких платформ умножает форматы исполняемых файлов и библиотек, поэтому сборка выполняется отдельно под каждую целевую систему. Его конфигурация - контейнеры Ubuntu в Docker плюс кросс-компиляция через mingw64 под Windows, связанные с помощью Bash и CMake. Настроить это мучительно, но затем всё собирается одной командой. Хрупкость этой схемы с противоположной стороны описана в win32-stable-abi: в Linux стабильным ABI, позволившим старым бинарникам игр продолжать работать, оказался Win32 через Wine, а не glibc.
С чего начать
Главный совет - не начинать. Сходите на геймджем и сделайте игру с нуля без какого-либо движка. Затем сходите на следующий и сделайте новую игру с нуля, ничего не используя повторно. Через несколько таких заходов вы поймаете себя на мысли, что вам очень пригодился бы конкретный готовый фрагмент кода - это желание и есть момент рождения вашего движка. Выделите повторяющиеся части, обобщите их по ходу дела и повторите процесс.
В результате вы получите движок, заточенный под ваши задачи, который вы знаете вдоль и поперёк, с которым работаете максимально эффективно и который можете менять по своему усмотрению. Это тот же тезис, что и в аргументе про раздражение в начале, только выведенный с другой стороны.