Как я создаю игры на своём 3D движке в одиночку

Много лет назад я занимался созданием маленьких Flash игр и публиковал их на сайте Newgrounds. Сейчас я делаю полноценные игры для ПК.

На сегодняшний день у меня 4 законченные коммерческие игры в Steam, и самая последняя из них — выпущенная в 2021 году Pilie Pals, о процессе создания которой я расскажу в этой статье. Я работал над игрой всего примерно 6 месяцев, по вечерам после работы и на выходных.

Pilie Pals — это игра-головоломка, в которой игрок возглавляет группу милых существ, способных складывать в стопки и носить разные предметы, и даже друг друга.
Pilie Pals — это игра-головоломка, в которой игрок возглавляет группу милых существ, способных складывать в стопки и носить разные предметы, и даже друг друга.

Я занимаюсь дизайном, программированием, графикой, звуками и музыкой в одиночку. Мне это нравится, и таким образом я могу часто переключаться с одного вида деятельности на другой, благодаря чему не теряю интерес к разработке игры.

Я написал собственный 3D игровой движок YUME, используя Haxe, C++ и OpenGL, и на данный момент он используется тремя моими играми. Подробности и причины создания собственного движка приведены в отдельной статье. Такой подход меня вполне удовлетворяет, и я не планирую его менять.

Разработка Pilie Pals

Трейлер к игре. Доступна бесплатная демо версия игры, а также есть страница в Steam.

В начале разработки Pilie Pals я скопировал папку проекта своей предыдущей игры Phantom Path и удалил все ресурсы и файлы кода, связанные с игрой. Остался "чистый" движок, с которым можно экспериментировать, чтобы создать прототип следующей игры.

Перед тем, как описывать создание Pilie Pals, я объясню несколько главных принципов работы моего движка.

Хранение игровых данных в текстовых файлах

Мой движок почти полностью основывается на внешних данных (data-driven). Это значит, что я создаю набор файлов с данными, а движок их читает и обрабатывает.

Я стараюсь избегать жёсткого прописывания чего-либо в исходном коде игры. По возможности, вся информация хранится в отдельных файлах: игровые объекты, уровни, переводы текстов, визуальные и звуковые эффекты, и даже некоторая игровая логика, написанная на собственном языке сценариев YumeScript. Большая часть данных хранится в формате JSON.

Самое большое преимущество такого подхода заключается в том, что я могу создавать и править игру, не выходя из неё. При изменении какого-либо файла с данными движок автоматически перегружает ту часть данных, которая изменилась, без необходимости перезапускать или пересобирать что-то вручную. То же самое происходит с другими ресурсами, например, с текстурами, 3D моделями и звуками.

Ещё один плюс — это потенциальная поддержка пользовательских модификаций игры. Для Pilie Pals это не так актуально, но это область, которую я хотел бы изучить в будущем.

Сущности

Два главных компонента сцены игры в моём движке — это сущности и карты.

Сущность — игровой объект, который я описываю в JSON файле, чтобы впредь создавать экземпляры объектов такого вида. Например, персонаж игрока — это сущность.

Файл описания сущности содержит информацию:

- Какие 3D модели содержит данная сущность, и как их отображать

- Какие анимации могут совершать модели этой сущности, и как осуществляются переходы из одного состояния в другое

- Какие эффекты может запускать данная сущность

- Какие звуки может воспроизводить данная сущность

- Какие области соприкосновения есть у данной сущности

- Какие у сущности могут быть состояния, и каково поведение сущности в разных состояниях

- Другие данные для использования в игровой логике: специальные тэги, группы, и т.д.

Каждый персонаж, дерево, ящик, элемент игры и декораций — это отдельная сущность.
Каждый персонаж, дерево, ящик, элемент игры и декораций — это отдельная сущность.

Стоит отметить, что у каждого экземпляра сущности есть собственная машина состояний, и у каждого состояния есть последовательность действий, которую сущность может проигрывать.

Например, можно задать состояние "walk" (ходьба) для сущности персонажа игры, и последовательность действий этого состояния может содержать команды, которые запускают нужную анимацию 3D модели, проигрывают звуки шагов и показывают эффект поднимающийся с земли пыли. Всё это описывается в текстовом файле, который можно редактировать и сразу тестировать, и это сильно ускоряет процесс разработки.

Карты

Карта — это файл данных, который содержит описание расположения сущностей. В этом файле также есть многоуровневая сетка "плиток", из которой можно построить ландшафт. Я не меняю файлы карт вручную — карты создаются с помощью встроенного редактора.

Встроенный редактор карт используется для создания всех уровней игры.
Встроенный редактор карт используется для создания всех уровней игры.

Так как движок знает всю информацию о сущностях, которые располагаются на карте, он в состоянии автоматически оптимизировать некоторые вещи. Например, если сущность является статичной и никогда не двигается (например, дерево или ландшафт), то движок автоматически объединяет её вместе с другими статичными сущностями, которые используют одинаковую текстуру, и создаёт одну общую 3D модель. Это значительно сокращает количество отображаемых видеокартой объектов, что сильно улучшает производительность игры.

Игровая логика

Некоторая часть игровой логики может быть описана в текстовых файлах, но она используется только для создания игровых сценариев, а не основной функциональности игры. Такая логика, как система соприкосновений, поведения искусственного интеллекта, правила игрового процесса и т.д. — программируется в исходном коде Haxe, который превращается в C++ при компиляции.

Есть 2 категории файлов кода, связанных с логикой:

- Процессоры логики сущностей — могут быть присоединены к отдельным сущностям, используются для обработки индивидуальной логики объектов (например, искусственного интеллекта). Не каждой сущности нужен процессор логики.

- Ядро — одиночный класс, который описывает общую логику правил игрового процесса.

Первый месяц: Прототип

У меня была идея о пошаговой игре-головоломке, в которой игрок может управлять сразу несколькими персонажами, способными поднимать и переносить всякие предметы. Логика игры должна была быть пошаговой, потому что я хотел записывать каждый игровой шаг, чтобы дать игроку возможность отменить свои шаги, т.е. вернуться в прошлое.

Сначала я написал ядро логики и реализовал пошаговую систему. Для каждой сущности, которая является частью головоломки, ядро выставляет состояние.

Игрок может выделить персонажа и выполнить действие (например, переместиться, или поднять предмет), которое меняет состояние игрового мира. Игра потом вычисляет, является ли возможным новое состояние мира (например, не столкнулся ли игрок с препятствием), и проверяет, нужна ли какая-либо реакция на это изменение, со стороны других элементов головоломки (например, если игрок положил какой-то предмет на кнопку, то у кнопки должно измениться состояние на "нажатая"). Если все проверки пройдены, то изменения применяются и добавляются в историю шагов.

Игрок может отменить последний шаг, просто вернувшись в предыдущее состояние мира.

У каждого элемента головоломки есть свое состояние.
У каждого элемента головоломки есть свое состояние.

В этом заключается основная функциональность игрового ядра. На самом деле, всё немного сложнее, потому что мне нужно обеспечить плавные, анимированные переходы между состояниями, позволить элементам быть переносимыми другими элементами, и делать другие интересные вещи — но всё это добавляется к базовому "фундаменту" логики игры.

Реализация такой системы и всех крайностей заняла у меня примерно неделю. Оставшееся время заняли размышления о том, какую игру я хочу сделать. В это время у меня появилась идея о том, что персонажи могли бы переносить других персонажей, и даже создавать стопки из друг друга.

Я создал 4 уровня игры, используя мой существующий редактор карт, чтобы убедиться, что игровой процесс действительно интересный. Результат мне понравился, и я продолжил разработку.

Второй месяц: Графика

Теперь у меня был рабочий прототип игры, и можно было начинать экспериментировать с художественными стилями. Я остановился на мультяшном визуальном стиле, и весь месяц занимался созданием 3D моделей, анимаций, эффектов, интерфейса, переходами, и т.д. Игра разбита на 5 тематических миров.

Уровень из четвёртого мира в Pilie Pals.
Уровень из четвёртого мира в Pilie Pals.

Я использую Blender для создания 3D моделей и анимаций, и GIMP для создания текстур.

Пластиковый вид игры достигнут с помощью написанного мною шейдера, который применяет заранее приготовленные данные об освещении к моделям. Работает это так: берётся заготовленная картинка освещённой сферы, применяется к некоторым частям модели, смешивая цветовые данные текстуры на основе нормалей модели в пространстве экрана.

Идея такого эффекта появилась у меня после работы с программами 3D моделирования и скульптуры. Подобный эффект в таких программах иногда называется MatCap. Мой подход заключается в том, что я смешиваю такой приём с традиционными алгоритмами освещения в реальном времени. Получается интересный эффект, который обрабатывается видеокартой очень быстро.

До и после применения предварительно "запечённых" данных освещения к моделям.
До и после применения предварительно "запечённых" данных освещения к моделям.

Третий месяц: Полировка

Весь месяц я улучшал User Experience игры: добивался плавности анимаций, хорошей чувствительности управления, чистоты графических элементов, удобности интерфейса.

Было реализовано меню паузы, меню выбора уровня, система последовательности уровней и системы сохранения — практически весь этот функционал у меня уже был создан для предыдущих игр, поэтому можно было повторно использовать часть этого кода, откорректировав некоторые визуальные вещи.

Карта мира, где игрок может выбрать следующий уровень.
Карта мира, где игрок может выбрать следующий уровень.

Работать над интерфейсами довольно утомительно, но плохой UX раздражает игроков, поэтому важно сделать всё правильно с самого начала.

Я решил сделать отполированный, полноценный "вертикальный срез" как можно быстрее. Таким образом я смог бы проверить, как бы выглядел готовый проект. Так как в игре на тот момент было мало контента, можно было свободно вносить глобальные изменения без особых проблем.

Четвёртый месяц: Музыка, звуки и демо версия

Я пишу музыку и создаю звуки в виртуальном модульном синтезаторе SunVox. Я — самоучка, и создание музыки у меня занимает довольно много времени.

Музыка из Pilie Pals в редакторе SunVox.
Музыка из Pilie Pals в редакторе SunVox.

В моём движке есть система динамического звукового окружения, которая может генерировать окружающий шум в реальном времени, используя подготовленные звуки (например, шум волн или крики птиц), меняя их частоту и воспроизводя в разных направлениях и в разных комбинациях. Эта информация описана в отдельном JSON файле.

На тот момент у меня была полностью "отполированная" игра, в которой было всего 4 уровня. Я создал ещё 6, и выпустил демо версию игры.

Так я стал собирать отзывы от игроков на ранней стадии разработки, и смог на их основе улучшить User Experience игры.

Примерно в это время я добавил систему подсказок в игру. О ней я подробно написал в отдельной статье.

Пятый и шестой месяцы: Контент игры

Следующие два месяца я делал новые уровни, добавлял новые игровые элементы, используя ту систему сущностей, которую я описал выше. В исходном коде игры уже практически не было никаких изменений, потому что ядро игры уже было полностью готово.

Эти два месяца я в основном работал в редакторе карт.

Уровень игры в редакторе карт.
Уровень игры в редакторе карт.

Вывод

В целом я удовлетворён результатом.

В начале разработки игры у меня уже было довольно чёткое представление о том, какую игру я хочу сделать. Поэтому процесс разработки прошёл намного плавнее и быстрее, чем обычно. Так я осознал важность наличия чёткой цели с самого начала.

Также оказалось хорошей идеей сделать отполированный вертикальный срез как можно скорее. Таким образом, я мог начать делать скриншоты, видео и даже выложить демо версию хорошего качества всего после 4 месяцев работы. Я получил несколько полезных отзывов от игроков ещё до того, как большая часть контента была готова, поэтому было проще вносить изменения, ничего при этом не ломая.

Один из ранних уровней в Pilie Pals.
Один из ранних уровней в Pilie Pals.

В этот раз я практически ничего не менял в ядре своего движка, и большая часть времени ушла непосредственно на создание игрового контента. Я буду продолжать использовать свой движок в будущих проектах.

155155
16 комментариев

Но, конечно, респект за смелость.

20

Круто!
Было бы интересно почитать, как разработал и как под капотом работает редактор карт)

5

Я буду продолжать использовать свой движок в будущих проектах.однозначно респект!

3

Милые существа…

2

Пластиковый вид игрыА по мне так больше напоминает какие-то вкусняшки :)

Играл в демку, мне идея зашла. Надеюсь когда-нибудь добраться и до полной версии.

2

Движка, я так понимаю, в открытом доступе нет? По крайней мере линк у тебя на сайте я не нашел

Код движка недоступен. Думаю, что движок слишком узкоспециализирован для общего потребления, а на создание полной документации и поддержку ушло бы много времени. Посчитал, что полезнее было писать такие статьи, в которых делюсь опытом самого процесса разработки.