Много лет назад я занимался созданием маленьких Flash игр и публиковал их на сайте Newgrounds. Сейчас я делаю полноценные игры для ПК.
На сегодняшний день у меня 4 законченные коммерческие игры в Steam, и самая последняя из них — выпущенная в 2021 году Pilie Pals, о процессе создания которой я расскажу в этой статье. Я работал над игрой всего примерно 6 месяцев, по вечерам после работы и на выходных.
![Pilie Pals — это игра-головоломка, в которой игрок возглавляет группу милых существ, способных складывать в стопки и носить разные предметы, и даже друг друга. Pilie Pals — это игра-головоломка, в которой игрок возглавляет группу милых существ, способных складывать в стопки и носить разные предметы, и даже друг друга.](https://habrastorage.org/getpro/habr/upload_files/cc6/41e/ec3/cc641eec3f038e9d8767dd68e91c0d22.jpg)
Я занимаюсь дизайном, программированием, графикой, звуками и музыкой в одиночку. Мне это нравится, и таким образом я могу часто переключаться с одного вида деятельности на другой, благодаря чему не теряю интерес к разработке игры.
Я написал собственный 3D игровой движок YUME, используя Haxe, C++ и OpenGL, и на данный момент он используется тремя моими играми. Подробности и причины создания собственного движка приведены в отдельной статье. Такой подход меня вполне удовлетворяет, и я не планирую его менять.
Разработка Pilie Pals
Трейлер к игре. Доступна бесплатная демо версия игры, а также есть страница в Steam.
В начале разработки Pilie Pals я скопировал папку проекта своей предыдущей игры Phantom Path и удалил все ресурсы и файлы кода, связанные с игрой. Остался "чистый" движок, с которым можно экспериментировать, чтобы создать прототип следующей игры.
Перед тем, как описывать создание Pilie Pals, я объясню несколько главных принципов работы моего движка.
Хранение игровых данных в текстовых файлах
Мой движок почти полностью основывается на внешних данных (data-driven). Это значит, что я создаю набор файлов с данными, а движок их читает и обрабатывает.
Я стараюсь избегать жёсткого прописывания чего-либо в исходном коде игры. По возможности, вся информация хранится в отдельных файлах: игровые объекты, уровни, переводы текстов, визуальные и звуковые эффекты, и даже некоторая игровая логика, написанная на собственном языке сценариев YumeScript. Большая часть данных хранится в формате JSON.
Самое большое преимущество такого подхода заключается в том, что я могу создавать и править игру, не выходя из неё. При изменении какого-либо файла с данными движок автоматически перегружает ту часть данных, которая изменилась, без необходимости перезапускать или пересобирать что-то вручную. То же самое происходит с другими ресурсами, например, с текстурами, 3D моделями и звуками.
Ещё один плюс — это потенциальная поддержка пользовательских модификаций игры. Недавно я выложил новое обновление для игры, которое добавляет русскую локализацию, а также возможность создавать неофициальные модификации для поддержки других языков.
Сущности
Два главных компонента сцены игры в моём движке — это сущности и карты.
Сущность — игровой объект, который я описываю в JSON файле, чтобы впредь создавать экземпляры объектов такого вида. Например, персонаж игрока — это сущность.
Файл описания сущности содержит информацию:
Какие 3D модели содержит данная сущность, и как их отображать
Какие анимации могут совершать модели этой сущности, и как осуществляются переходы из одного состояния в другое
Какие эффекты может запускать данная сущность
Какие звуки может воспроизводить данная сущность
Какие области соприкосновения есть у данной сущности
Какие у сущности могут быть состояния, и каково поведение сущности в разных состояниях
Другие данные для использования в игровой логике: специальные тэги, группы, и т.д.
![Каждый персонаж, дерево, ящик, элемент игры и декораций — это отдельная сущность. Каждый персонаж, дерево, ящик, элемент игры и декораций — это отдельная сущность.](https://habrastorage.org/getpro/habr/upload_files/02c/142/d11/02c142d114cba4a00fe1f831abaa8de4.jpg)
Стоит отметить, что у каждого экземпляра сущности есть собственная машина состояний, и у каждого состояния есть последовательность действий, которую сущность может проигрывать.
Например, можно задать состояние "walk" (ходьба) для сущности персонажа игры, и последовательность действий этого состояния может содержать команды, которые запускают нужную анимацию 3D модели, проигрывают звуки шагов и показывают эффект поднимающийся с земли пыли. Всё это описывается в текстовом файле, который можно редактировать и сразу тестировать, и это сильно ускоряет процесс разработки.
Карты
Карта — это файл данных, который содержит описание расположения сущностей. В этом файле также есть многоуровневая сетка "плиток", из которой можно построить ландшафт. Я не меняю файлы карт вручную — карты создаются с помощью встроенного редактора.
![Встроенный редактор карт используется для создания всех уровней игры. Встроенный редактор карт используется для создания всех уровней игры.](https://habrastorage.org/getpro/habr/upload_files/1af/8d8/21d/1af8d821d4966327d2db56d479c75fb0.jpg)
Так как движок знает всю информацию о сущностях, которые располагаются на карте, он в состоянии автоматически оптимизировать некоторые вещи. Например, если сущность является статичной и никогда не двигается (например, дерево или ландшафт), то движок автоматически объединяет её вместе с другими статичными сущностями, которые используют одинаковую текстуру, и создаёт одну общую 3D модель. Это значительно сокращает количество отображаемых видеокартой объектов, что сильно улучшает производительность игры.
Игровая логика
Некоторая часть игровой логики может быть описана в текстовых файлах, но она используется только для создания игровых сценариев, а не основной функциональности игры. Такая логика, как система соприкосновений, поведения искусственного интеллекта, правила игрового процесса и т.д. — программируется в исходном коде Haxe, который превращается в C++ при компиляции.
Есть 2 категории файлов кода, связанных с логикой:
Процессоры логики сущностей — могут быть присоединены к отдельным сущностям, используются для обработки индивидуальной логики объектов (например, искусственного интеллекта). Не каждой сущности нужен процессор логики.
Ядро — одиночный класс, который описывает общую логику правил игрового процесса.
Первый месяц: Прототип
У меня была идея о пошаговой игре-головоломке, в которой игрок может управлять сразу несколькими персонажами, способными поднимать и переносить всякие предметы. Логика игры должна была быть пошаговой, потому что я хотел записывать каждый игровой шаг, чтобы дать игроку возможность отменить свои шаги, т.е. вернуться в прошлое.
Сначала я написал ядро логики и реализовал пошаговую систему. Для каждой сущности, которая является частью головоломки, ядро выставляет состояние.
Игрок может выделить персонажа и выполнить действие (например, переместиться, или поднять предмет), которое меняет состояние игрового мира. Игра потом вычисляет, является ли возможным новое состояние мира (например, не столкнулся ли игрок с препятствием), и проверяет, нужна ли какая-либо реакция на это изменение, со стороны других элементов головоломки (например, если игрок положил какой-то предмет на кнопку, то у кнопки должно измениться состояние на "нажатая"). Если все проверки пройдены, то изменения применяются и добавляются в историю шагов.
Игрок может отменить последний шаг, просто вернувшись в предыдущее состояние мира.
![У каждого элемента головоломки есть свое состояние. У каждого элемента головоломки есть свое состояние.](https://habrastorage.org/getpro/habr/upload_files/795/be4/16e/795be416ea777717e95541b3debda2d0.jpg)
В этом заключается основная функциональность игрового ядра. На самом деле, всё немного сложнее, потому что мне нужно обеспечить плавные, анимированные переходы между состояниями, позволить элементам быть переносимыми другими элементами, и делать другие интересные вещи — но всё это добавляется к базовому "фундаменту" логики игры.
Реализация такой системы и всех крайностей заняла у меня примерно неделю. Оставшееся время заняли размышления о том, какую игру я хочу сделать. В это время у меня появилась идея о том, что персонажи могли бы переносить других персонажей, и даже создавать стопки из друг друга.
Я создал 4 уровня игры, используя мой существующий редактор карт, чтобы убедиться, что игровой процесс действительно интересный. Результат мне понравился, и я продолжил разработку.
Второй месяц: Графика
Теперь у меня был рабочий прототип игры, и можно было начинать экспериментировать с художественными стилями. Я остановился на мультяшном визуальном стиле, и весь месяц занимался созданием 3D моделей, анимаций, эффектов, интерфейса, переходами, и т.д. Игра разбита на 5 тематических миров.
![Уровень из четвёртого мира в Pilie Pals. Уровень из четвёртого мира в Pilie Pals.](https://habrastorage.org/getpro/habr/upload_files/d29/fa2/591/d29fa25917d0e696c3fd97d4c9baea3b.jpg)
Я использую Blender для создания 3D моделей и анимаций, и GIMP для создания текстур.
Пластиковый вид игры достигнут с помощью написанного мною шейдера, который применяет заранее приготовленные данные об освещении к моделям. Работает это так: берётся заготовленная картинка освещённой сферы, применяется к некоторым частям модели, смешивая цветовые данные текстуры на основе нормалей модели в пространстве экрана.
Идея такого эффекта появилась у меня после работы с программами 3D моделирования и скульптуры. Подобный эффект в таких программах иногда называется MatCap. Мой подход заключается в том, что я смешиваю такой приём с традиционными алгоритмами освещения в реальном времени. Получается интересный эффект, который обрабатывается видеокартой очень быстро.
![До и после применения предварительно "запечённых" данных освещения к моделям. До и после применения предварительно "запечённых" данных освещения к моделям.](https://habrastorage.org/getpro/habr/upload_files/d39/f72/f77/d39f72f77a5a384bd24ba4bf10360027.jpg)
Третий месяц: Полировка
Весь месяц я улучшал User Experience игры: добивался плавности анимаций, хорошей чувствительности управления, чистоты графических элементов, удобности интерфейса.
Было реализовано меню паузы, меню выбора уровня, система последовательности уровней и системы сохранения — практически весь этот функционал у меня уже был создан для предыдущих игр, поэтому можно было повторно использовать часть этого кода, откорректировав некоторые визуальные вещи.
![Карта мира, где игрок может выбрать следующий уровень. Карта мира, где игрок может выбрать следующий уровень.](https://habrastorage.org/getpro/habr/upload_files/515/136/7dc/5151367dc69bad667a710529b68e2aff.jpg)
Работать над интерфейсами довольно утомительно, но плохой UX раздражает игроков, поэтому важно сделать всё правильно с самого начала.
Я решил сделать отполированный, полноценный "вертикальный срез" как можно быстрее. Таким образом я смог бы проверить, как бы выглядел готовый проект. Так как в игре на тот момент было мало контента, можно было свободно вносить глобальные изменения без особых проблем.
Четвёртый месяц: Музыка, звуки и демо версия
Я пишу музыку и создаю звуки в виртуальном модульном синтезаторе SunVox. Я — самоучка, и создание музыки у меня занимает довольно много времени.
![Музыка из Pilie Pals в редакторе SunVox. Музыка из Pilie Pals в редакторе SunVox.](https://habrastorage.org/getpro/habr/upload_files/ac5/4a9/611/ac54a96111e94546b0ad4c29f82044df.jpg)
В моём движке есть система динамического звукового окружения, которая может генерировать окружающий шум в реальном времени, используя подготовленные звуки (например, шум волн или крики птиц), меняя их частоту и воспроизводя в разных направлениях и в разных комбинациях. Эта информация описана в отдельном JSON файле.
На тот момент у меня была полностью "отполированная" игра, в которой было всего 4 уровня. Я создал ещё 6, и выпустил демо версию игры.
Так я стал собирать отзывы от игроков на ранней стадии разработки, и смог на их основе улучшить User Experience игры.
Примерно в это время я добавил систему подсказок в игру. О ней я подробно написал в отдельной статье.
Пятый и шестой месяцы: Контент игры
Следующие два месяца я делал новые уровни, добавлял новые игровые элементы, используя ту систему сущностей, которую я описал выше. В исходном коде игры уже практически не было никаких изменений, потому что ядро игры уже было полностью готово.
Эти два месяца я в основном работал в редакторе карт.
![Уровень игры в редакторе карт. Уровень игры в редакторе карт.](https://habrastorage.org/getpro/habr/upload_files/ce4/f8e/9f1/ce4f8e9f1bbe0e1e9dce95668fc84ad0.jpg)
Вывод
В целом я удовлетворён результатом.
В начале разработки игры у меня уже было довольно чёткое представление о том, какую игру я хочу сделать. Поэтому процесс разработки прошёл намного плавнее и быстрее, чем обычно. Так я осознал важность наличия чёткой цели с самого начала.
Также оказалось хорошей идеей сделать отполированный вертикальный срез как можно скорее. Таким образом, я мог начать делать скриншоты, видео и даже выложить демо версию хорошего качества всего после 4 месяцев работы. Я получил несколько полезных отзывов от игроков ещё до того, как большая часть контента была готова, поэтому было проще вносить изменения, ничего при этом не ломая.
![Один из ранних уровней в Pilie Pals. Один из ранних уровней в Pilie Pals.](https://habrastorage.org/getpro/habr/upload_files/834/9c6/d6d/8349c6d6d7399f6fa7550388a4145e96.jpg)
В этот раз я практически ничего не менял в ядре своего движка, и большая часть времени ушла непосредственно на создание игрового контента. Я буду продолжать использовать свой движок в будущих проектах.