Наш игровой движок Heaps.io и набор инструментов и технологий, на котором он основан, являются результатом опыта, накопленного за двадцать лет, посвященных созданию игр: сначала в компании Motion-Twin (создатели Dead Cells), а с 2012 года в Shiro Games (Evoland, Northgard и Darksburg).
Названные игры (2D и 3D) были созданы с использованием стека библиотек и инструментов, исходный код которых был открытым с самого начала, и которые продолжают развиваться и поддерживаться.
Так как меня часто спрашивают о том, как мы создаем игры, то я подумал, что было бы неплохо поделиться подробностями обо всех элементах технологического стека Shiro Games. Он прекрасно подходит для наших задач, так что возможно он может подойти и другим компаниям.
Сообщество Haxe / Heaps
Если у вас есть какие-либо вопросы или вы просто хотите обсудить представленные в этой статье технологии, вы можете связаться с сообществом Haxe / Heaps:
- на канале #heaps в Discord
- задать вопросы, связанные с языком Haxe, вы можете на форуме Haxe
- а для вопросов по Heaps есть соответствующий форум
Нативный слой
Нативный слой в основном написан на C с небольшой долей C++. В повседневной работе нам редко приходится напрямую иметь с ним дело, поскольку вместо C / C++ мы работаем с языками более высокого уровня, где серьезные сбои (в основном) сопровождаются информативными сообщениями об ошибках.
Производительность очень важна для нас, потому что — хотя мы и не создаем AAA-игры — мы всё-таки хотим получить отличную графику и захватывающий геймплей в 60 фпс, и не хотим тратить при этом драгоценное время наших кодеров на микрооптимизации из-за ограничений низкоуровневого движка.
HashLink VM
Основным компонентом этой стратегии является виртуальная машина HashLink — быстрая строго типизированная виртуальная машина для языка программирования Haxe. Ее можно сравнить с JavaVM или Mono (используется в Unity), но при этом HashLink VM больше ориентирована на игры в реальном времени.
Игра компилируется в кроссплатформенный файл .hl
, который можно запустить с помощью HashLinkVM JIT. Также байткод HashLink может быть сконвертирован напрямую в код на C, который затем можно скомпилировать с помощью любого компилятора — такой путь мы используем для консольных портов наших игр для PlayStation, Xbox и Nintendo Switch.
HashLink VM отлично работает как для классического объектно-ориентированного, так и для функционального стилей программирования. Она хорошо себя показывает в вычислениях с плавающей точкой, что важно для игр. Также данная виртуальная машина была разработана таким образом, чтобы дополнительные затраты памяти для работы сборщика мусора были минимальными. Например, наша 3D-игра Northgard использует менее 500 МБ памяти.
Так как HashLink — виртуальная машина, то это значит, что для вашей игры (или приложения) не будет различия между отладочной и релизной сборками (только если вы не скомпилируете свой код в C), игра всегда будет работать так же, как и на компьютерах игроков, на полной скорости, и при этом всегда можно будет получить полный стек вызовов.
Нативные библиотеки
Хотя HashLink поставляется только с небольшой стандартной библиотекой, ее возможности могут быть расширены с помощью дополнительных библиотек (написанных на C), предоставляющих доступ к новым API. Написание таких библиотек потребует от разработчика некоторых знаний об устройстве виртуальной машины и о том, как работать со сборщиком мусора, но это довольно несложный процесс. При правильной реализации можно легко изолировать ошибки, возникающие на низком уровне, от ошибок в логике приложения.
На данный момент с HashLink распространяются такие нативные библиотеки как: SDL2, DirectX11, OpenAL (работа со звуком), LibUV (сокеты), SSL (шифрование) и FMT (для работы с Zip, Ogg, Png, Jpg) а также ряд библиотек для работы с другими форматами файлов.
Исходный код перечисленных библиотек открыт и является частью репозитория HashLink.
Также есть отдельная библиотека для работы с API Steam, и библиотеки для интеграции с игровыми консолями, доступ к которым может быть предоставлен только зарегистрированным разработчикам.
Таким образом, при необходимости вы легко сможете самостоятельно расширить виртуальную машину с помощью собственных нативных библиотек.
Нативные инструменты
Низкоуровневые инструменты необходимы для анализа производительности разрабатываемых приложений. В Shiro Games мы используем следующий набор инструментов:
- В HashLink VM есть встроенный отладчик, имеющий интеграцию с Visual Studio Code
- Для анализа использования CPU недавно был разработан HashLink CPU Profiler. Работа данного профилировщика не замедляет работу анализируемого приложения на HashLink
- Для измерений потребления памяти и обнаружения утечек, вы можете либо замерить все выполненные аллокации, либо сделать дамп памяти для последующего анализа с помощью Memory Profile API (к сожалению данное API на данный момент не задокументировано)
- Для измерения производительности графической подсистемы мы используем инструменты, предоставляемые вендорами GPU, например, NVIDIA Nsight.
Уровень языка программирования
Рассмотрев нативный уровень (обозначен на схеме зеленым), перейдем по стеку к уровню языка программирования.
Для разработки игр и инструментов мы используем язык программирования Haxe. Haxe предлагает отличное сочетание строго типизированного объектно-ориентированного и функционального программирования, а также продвинутую систему макросов, которые используются в некоторых высокоуровневых библиотеках, представленных далее.
Конечно, как главный разработчик языка Haxe, я немного предвзят, но каждому программисту в Shiro действительно нравится работать с Haxe, и это критически важный инструмент для нашей повседневной работы. Кроме того, мы наняли разработчиков с различным опытом программирования (C++, C#, JavaScript, Python, Java и т.д.), и все они смогли быстро адаптироваться к Haxe и писать эффективный код.
Haxe — это кроссплатформенный язык программирования, который может компилировать код под множество различных платформ. В Shiro мы используем в основном две платформы: HashLink для наших игр и JavaScript для наших инструментов (подробнее об этом далее).
Некоторые популярные инди-игры также были созданы с использованием Haxe (но без HashLink или других представленных в данной статье инструментов), например: Papers Please, Brawlhalla, Dicey Dungeons и др.
Haxe поддерживается как независимый проект с открытым исходным кодом. Поддержка осуществляется Haxe Foundation, который финансируется несколькими крупными компаниями, использующими Haxe для разработки кроссплатформенных приложений и игр.
Узнать больше о Haxe вы можете на его официальном сайте.
Игровой движок: Heaps.io
Heaps.io — игровой движок, лежащий в основе игр Shiro Games. Он обеспечивает:
- 2D рендеринг
- 3D рендеринг
- Обработка звука
- Управление (клавиатура, мышь, геймпад)
- Управление ресурсами.
Он построен так, чтобы отделить реализацию низкоуровневых функций для поддержки платформ от графической логики / данных среднего уровня. Такая архитектура позволяет интегрировать новый рендерер или поддержку новой платформы — для этого достаточно просто портировать несколько классов (при условии что у вас уже есть соответствующие нативные библиотеки для HashLink). На данный момент Heaps.io поддерживает следующие платформы / графические API:
- HashLink и DirectX11
- HashLink и OpenGL / SDL2
- HashLink / C и NVN (нативное графическое API в Nintendo Switch SDK)
- HashLink / C и GNM (нативное графическое API в PS4 SDK)
- HashLink / C и Xbox One SDK
- JavaScript и WebGL2
Примечание: обозначение "HashLink / C" означает, что для указанной платформы возможно использовать только компиляцию из байткода HashLink в C (JIT VM на данных платформах не доступен).
Heaps.io задумывался как легковесный движок, который можно легко адаптировать под нужды пользователей. Он предоставляет 2D / 3D граф сцены, и поведение каждого объекта на сцене можно доработать. Рендерер и система освещения также могут быть полностью заменены, что позволяет написать собственный конвейер рендеринга, отвечающий требованиям конкретной игры.
И 2D и 3D используют аппаратное ускорение на GPU и основаны на GPU-шейдерах. Heaps поставляется с собственным встроенным языком шейдеров HxSL. HxSL — мощный инструмент, поскольку вместо необходимости написания одного большого шейдера он позволяет написать отдельные небольшие шейдерные эффекты, которые затем собираются вместе и оптимизируются во время выполнения программы.
Система управления ресурсами в Heaps.io вынесена в отдельный расширяемый фреймворк. Кроме того, Heaps.io предоставляет средства для «запекания ресурсов» — автоматической конвертации и упаковки ресурсов в более подходящий для движка формат (например, для ресурсов, получаемых на выходе из Photoshop, Maya, Blender и т.д.)
Больше информации о Heaps вы найдете на его официальном сайте, в разделе документации.
Редактор HIDE
HIDE (Heaps IDE) — это отдельное приложение, которое позволяет создавать средства для просмотра и редактирования 2D / 3D-контента.
HIDE — это HTML5-приложение, в котором запущен инстанс движка Heaps.io в режиме WebGL2, что позволяет очень быстро разрабатывать пользовательские интерфейсы, используя Haxe и HTML / CSS.
На данный момент HIDE предоставляет следующие средства:
- древовидный список ресурсов
- просмотрщик 2D текстур
- просмотрщик 3D моделей в формате FBX и редактор материалов
- редактор 2D и 3D эффектов на основе временной шкалы (timeline based effects)
- редактор 2D и 3D частиц
- редактор 3D-уровней (на скриншоте ниже показан уровень Darksburg)
- редактор Префабов
- редактор скриптов (с использованием hscript)
- редактор графа шейдеров [работа над ним пока что не окончена]
HIDE ориентирован на обработку данных (data-oriented) и основан на модели Префабов (класс hrt.prefab.Prefab
). Эта модель хранит данные редактора и может по запросу создавать экземпляры эффектов, уровней и т.д.; также она является поставщиком данных для движка, который затем обрабатывает их в соответствии с заложенной вами игровой логикой.
Поскольку модель Префабов расширяемая и в HIDE реализована поддержка плагинов, то в HIDE есть возможность добавлять пользовательские компоненты и редакторы.
Исходный код HIDE разделен между пакетами hide
(содержит код IDE), и hrt
(содержит классы, которые можно использовать как часть игрового клиента).
К сожалению, в настоящий момент документация для HIDE практически отсутствует и мне еще предстоит поработать над ней.
DomKit UI Toolkit
DomKit — это наш фреймворк для написания компонентов пользовательского интерфейса. На данный момент это самое последнее дополнение к нашему технологическому стеку, поэтому он все еще дорабатывается.
Он позволяет описывать пользовательский интерфейс с помощью XHTML-разметки непосредственно в коде игры, что обеспечивает возможность напрямую связывать строго типизированные данные с логикой игрового процесса. Кроме этого, в DomKit есть возможность стилизации интерфейсов с помощью CSS, прямо как в веб-разработке.
Используемая семантика CSS частично соответствует стандартам HTML5, но модель разметки специфична для Heaps.io и больше подходит для UI / UX в играх.
Также вы можете объявлять собственные компоненты, добавлять дополнительные свойства и код, описывающий как CSS должен применяться к свойствам ваших компонентов.
Кроме того, библиотека DomKit полностью автономна и может использоваться совместно с любыми UI фреймворками (но в Heaps.io поддержка DomKit уже реализована).
Узнать больше о DomKit вы можете из его документации.
Castle DB
Castle DB — один из самых важных инструментов для повышения производительности, который используется в Shiro Game. Это статическая структурированная база данных, с которой работают наши геймдизайнеры — редактируют все данные, используемые в игре (списки предметов, локаций, NPC, деревьев технологий, навыков и т.д.).
Используя IDE Castle DB, можно задавать и изменять структуры данных, а затем вводить данные в соответствии с описанием этих структур. В этом отношении IDE Castle DB является своего рода специализированным редактором электронных таблиц для игр.
Кроме того, используя макросы Haxe, можно напрямую получить все структуры данных, объявленные в CDB-файле, а также перечисления (enums) для всех уникальных идентификаторов для различных типов игровых объектов / уровней / и т.д.
// Data.hx
private typedef Init = haxe.macro.MacroType<[cdb.Module.build("data.cdb")]>;
Данные в CDB хранятся в виде текстового файла в формате JSON, что дает возможность совместно работать с данными, используя систему контроля версий (слияние, различие, разрешение конфликтов и т.д.).
Castle DB раньше был отдельным редактором (его старую версию можно скачать с его веб-сайта), но теперь он интегрирован в HIDE, благодаря чему можно использовать данные CDB для Префабов в редакторе уровней и т.д.
Узнать больше о Castle DB вы можете на сайте castledb.org.
HScript
HScript — это небольшой парсер и интерпретатор скриптов, синтаксис которых основан на синтаксисе Haxe. Таким образом у геймдизайнеров есть возможность самостоятельно вносить изменения в коде, не прибегая к помощи программистов и не пересобирая клиент.
Одна из самых приятных вещей в HScript — это то, что он позволяет вносить изменения в скрипт на этапе между парсингом и выполнением скрипта. Например, класс Async преобразует скрипт в асинхронный код.
Совсем недавно я добавил в HScript возможность проверки типов в скриптах, данный механизм работает примерно также как в компиляторе Haxe. Это позволило добавить в редактор скриптов, встроенный в HIDE, автодополнение кода и проверку типов прямо во время их написания (о том, как добавить поддержку собственных типов, можно почитать в вики), что должно помочь геймдизайнерам совершать меньше ошибок и повысить их продуктивность.
HScript также интегрирован в CDB / Hide, что позволяет назначать объектам скрипты и т.д.
Исходный код библиотеки доступен на github, а установить ее можно с помощью команды haxelib install hscript
.
HxBit
HxBit — это библиотека для сериализации и сетевой синхронизации. Она позволяет помечать свойства ваших классов, которые должны быть сериализованы (для сохранения) и / или переданы по сети при их изменении (для многопользовательских игр).
Полная документация доступна в вики.
MPMan
MPMan построен поверх HxBit для сетевой части и содержит дополнительные служебные классы. Это единственная библиотека с закрытым исходным кодом в составе фреймворка, поскольку она содержит конфиденциальную информацию о нашей инфраструктуре для поддержки многопользовательских режимов.
Данная библиотека обеспечивает следующее:
- аутентификация игрока на разных платформах
- многопользовательская система лобби, рейтинги, запрос инстансов игровых серверов
- приглашения для многопользовательской игры
- достижения
- обнаружение DLC и всплывающие окна магазинов
- хранение (кроссплатформенная обработка пользовательских сохранений).
Заключение
Вот и все (пока). Я надеюсь, что после прочтения этой статьи у вас сформировалось некоторое представление о том, как мы делаем игры в Shiro. Также я надеюсь, что заинтересовал вас попробовать некоторые, если не все, из представленных инструментов.
Я написал и продолжаю поддерживать многие из этих инструментов. В этом деле хорошую помощь оказывают и программисты Shiro и члены сообщества Haxe, и я благодарен им за это! Я думаю, что для отличной работы необходимо иметь хорошие инструменты, и каждый из инструментов, представленных здесь, оказался полезным в нескольких реальных проектах. Я вполне доволен достигнутыми результатами, и поэтому решил, что пришло время рассказать о них.
Также я верю в совместную работу на проектами, и в то, что открытие исходного кода также помогает развивать проекты: не только благодаря возможности получить помощь от сторонних разработчиков, но и благодаря тому, что открытие исходного кода обязывает меня заниматься решением таких важных вопросов как написание документации и сглаживание имеющихся "шероховатостей" насколько это возможно.
Все инструменты в описанном технологическом стеке использовались в реальных проектах и используются для разработки еще не анонсированных игр. Они достаточно стабильны, без серьезных проблем, но, конечно, всегда есть возможность найти баги для какого-то конкретного случая. Поскольку я работаю над несколькими проектами одновременно, то мне не всегда удается быстро ответить на сообщения о найденных ошибках или пулл-реквестах, но по крайней мере у вас есть все исходники и, следовательно, возможность самостоятельно все исправить. Одна из вещей, которые лично мне больше всего не нравятся — это ситуации, когда нет возможности понять / исправить найденные ошибки.
Наконец, хочу упомянуть, что Shiro Games активно ищет разработчиков для работы над увлекательными, но еще не анонсированными проектами. Вы можете связаться со мной в Twitter и посмотреть список наших вакансий.
Suvitruf
Ещё интересно, что там за физически движок.
P.S. занятно, что они позиционируют его как «игровой движок», хотя по факту это, скорее, фреймворк.
stilic
Unity — это набор библиотек/инструментов и ипользование VM .NET.
А HashLink VM — это просто VM.
Если уж сравнивать Unity как совакупность то не с голой HashLink VM, а с Heaps.io и пр. обертками.
Suvitruf
Unity я имел ввиду именно в контексте виртуалки и Mono.
Zaphy Автор
Сравнений с Unity я нигде не нашел. Но автор оригинала также является автором HashLink, так что могу предположить, что он проектировал VM с этим расчетом
По поводу скорости работы и вычислений могу только сказать, что ее достаточно для работы упомянутых игр на Heaps.io (они выходили на Nintendo Switch, XBox и Playstation, а Dead Cells должен выйти этим летом и на Android)
В качестве физического движка можно использовать Bullet
domix32
а в чем принципиальная разница?
Zaphy Автор
Копипаста с gamedev.ru:
Фреймворк (англ. framework — каркас, структура) — структура программной системы; программное обеспечение, облегчающее разработку и объединение разных компонентов большого программного проекта. Можно также говорить о каркасном подходе как о подходе к построению программ, где любая конфигурация программы строится из двух частей: первая, постоянная часть — сам фреймворк, не меняющийся от конфигурации к конфигурации и несущий в себе гнезда, в которых размещается вторая, переменная часть — сменные модули (или точки расширения).
Фреймворк накладывает некие ограничения на структуру программы, и пользователю предлагается его расширять до достижения требуемого результата.
Движок (игровой движок, game engine) — программное ядро комплексной программной системы (игры), содержащее базовую функциональность игры, но, при этом, не включающее код, специфичный для геймплейной функциональности конкретной игры.
Движок — комплекс программных модулей, облегчающий создание конечного продукта вплоть до полного отсутствия программирования (визуальное программирование).
Движок может включать в себя также набор инструментов (редактор уровней, редактор логики, упаковщик ресурсов и т.д.)
Таким образом, фреймворк — это только общая структура будущей игры, каркас, вокруг которого она будет нарастать. В качестве примеров фреймворков можно привести LibGDX, Love2D
А движок — более сложная система, состоящая из множества специализированных модулей (физика, анимация, редакторы, просмотрщики ресурсов и многое другое). Иногда движки затачиваются под разработку игр определенных жанров (или по крайней мере лучше подходят для них). Примеры движков: Unity3D, Unreal Engine, движки попроще — GameMaker, Stencyl
domix32
Звучит как "нет редактора — не движок". Что одно штука с дырками, что другое — сиди расширения свои надстраивай. Где-то батарейки уже в комплекте, разной степени заменяемости, а где-то без, что собственно не мешает поставить свои.
Zaphy Автор
Я написал, что движок может включать в себя редактор, но это не обязательно.
Да, согласен, что четкой границы между фреймворками и движками не провести. Дело в этой самой степени заменяемости батареек.
Но где-то эта граница все-таки есть, может быть она субъективная. Но назвать Unreal Engine фреймворком, a LibGDX — движком я себя никак не смогу заставить.
Но вообще это тема больше для холиворов как мне кажется.