Как стать автором
Обновить

Постмортем создания кроссплатформенного казуального движка

Время на прочтение5 мин
Количество просмотров2.4K
По долгу службы встала необходимость в реализации кроссплатформенного движка для казуальных игр (по большей части, квестов). В этой статье я постараюсь рассказать про некоторые нетривиальные вопросы, которые мы решили по ходу разработки.

Введение


Движок должен поддерживать как минимум три платформы: PC, MacOS X и iOS. Он применяется для создания квестов, так что больший упор — на визуальную красоту: системы частиц, анимации, видео.

Основой движка являются следующие сущности:
— основной класс движка, который отвечает за создание окна, пользовательский ввод, получение/потерю фокуса, управление всеми остальными частями движка;
— класс отрисовки;
— менеджер ресурсов — загрузка, хранение, выгрузка ресурсов (текстуры, шрифты).

Эти три большие сущности сами по себе являются полностью самодостаточными и не имеют внешних зависимостей от остальной части движка, так что, когда мы делали аркаду для iOS, мы просто взяли их и накрутили сверху action-обёртку.

Поверх основы была создана непосредственно игровая (квестовая) часть. Основной идеей при её создании было единообразие управления, создания и загрузки игровых сущностей.
Это было введение, теперь обо всем поподробнее.

Реализация кроссплатформенности


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

Для того чтобы упростить портирование мы постарались локализовать весь платформозависимый код как можно в меньшем количестве классов, а основную работу вести с общим для всех платформ кодом. В итоге платформозависимыми классами у нас стали: текстура, класс отрисовки, менеджер окна, доступ к файловой системе и звук. Для того чтобы весь остальной код не знал об использованной платформе, мы вынесли создание низкоуровневых объектов в фабрику, которая создавала нужный объект в зависимости от управляющих директив. Создание низкоуровневых объектов напрямую (new) было запрещено.

PC- и MacOS X-реализации были сделаны с помощью библиотеки Playground, потому что мы работаем с этой библиотекой достаточно давно и можем быть уверены, что получим игру, работающую у очень большого количества пользователей. Для iOS реализовали всё сами на OpenGL ES. Там никаких сложностей, кроме отрисовки в текстуру и доступа к произвольному пикселю текстуры, у нас не возникло.

Структуризация игровых сущностей


Об этом я уже писал один пост в песочницу, но, видимо, он был не очень.

Идея в том, чтобы все игровые сущности были в общей системе и имели один общий интерфейс. Любую игру можно разделить на движок и собственно игру. Так вот собственно игра и любые сущности внутри неё у нас унаследованы от одного общего класса объекта, определенного в движке и реализующего все базовые операции: процесс, инпут, отрисовка, загрузка, выгрузка, управление детьми. Каждый объект имеет уникальное имя, которое генерируется или задается дизайнерами уровней. Имея один общий интерфейс, любой из программистов может поправить любой код, потому что всё везде единообразно. Сделав уже три игры, мы проанализировали игровой код и заметили, что количество уникальных для игровых объектов методов, которые не реализованы в базовом объекте не больше одного-двух на класс. Не представляю, как мы до этого жили с этим зоопарком классов и методов внутри них.

Второй стороной этой идеи был в создании древовидной структуры. Каждый объект кому-то принадлежит, кто-то его создал, и кто-то его удалит. Все объекты прицепляются один к другому. На верхушке этой иерархии два объекта: “корень” и “хранилище”. Всё, что прицеплено к корню, — рисуется, процессится, обрабатывает ввод; все, что прицепляется к корню, автоматически загружает свои ресурсы; то, что отцепляется от него, — выгружает ресурсы. “Хранилище” просто хранит внутри себя то, что пока не нужно, но может пригодиться и времени на загрузку не будет; также в “хранилище” складируются элементы, состояние которых нужно запомнить и показать пользователю в точно таком же виде позже. То есть, таская объекты между этими двумя супер-объектами, мы управляем тем, что отображается на экране. Доступ до любого объекта осуществляется по его уникальному имени благодаря рекурсивной процедуре поиска.

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

Игровой редактор


Редакторы мы у себя пишем на С#. Это удобно, быстро и дизайнеры уровней рады стандартному виндовому интерфейсу и хоткеям. А игры мы пишем на С++. И вот тут начинают возникать проблемы…

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

Сначала мы думали про различные библиотеки контролов для OpenGL и DirectX, но дизайнеры начали канючить и ныть, что им не нравится, не юзерфрендли и всё такое. Мы пошли им навстречу — их все же раз в пять больше чем программистов.

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

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

Заключение


Это были основные моменты, которые позволили нам реализовать простой и переносимый игровой движок. Было ещё много мелочей, которые будут оформлены в виде отдельной статьи.
Теги:
Хабы:
+2
Комментарии14

Публикации

Изменить настройки темы

Истории

Работа

Ближайшие события