Moai SDK 1.5 — кроссплатформенный 2д игровой движок


Сегодня я хочу рассказать об одном малоизвестном игровом движке, который мы используем уже год для кроссплатформенной разработки мобильных игр. Для 2д он нас полностью устраивает, а единственным конкурентом может быть только Unity3d из-за своего редактора. Отсутствие должного внимания к MOAI SDK, очевидно, связано с высоким порогом входа — сами разработчики (Zipline Games) позиционируют свой продукт как «The mobile platform for pro game developers», хотя разобравшись с установкой и настройкой окружения можно очень быстро и просто клепать игры на Lua.

Чем же мне так понравился MOAI:
  • Движок написан на C++, игровая логика пишется на Lua. Есть поддержка luajit (на iOS только в режиме интерпретатора)
  • Открытый исходный код. Лицензия CPAL — необходимо указывать логотип, название, авторов и ссылку на MOAI на загрузочном экране или в титрах
  • Кроссплатформенный: Windows, Mac OS X, Linux, iOS, Android, OUYA, а также есть эксперименты с html5 через emscripten. Разработка поддерживается на Windows, Mac OS X и Linux. Для сборки под iOS нужен Mac. Теоретически, добавить новую платформу не сложно — надо написать «хост», который создаст OpenGL контекст и будет вызывать методы обработки ввода
  • Lua API очень низкоуровневый. Это и плюс, и минус. Для эффективной разработки просто необходим Lua фреймворк более высокого уровня
  • Внутри используются крутые алгоритмы и технологии. Рендерер с автоматическим батчингом (cocos2d вроде тоже этому научился). Action tree — все что периодически обновляется (анимации, физика) представлено в виде дерева, родительские узлы передают прошедшее время (дельту) своим детям; это позволяет выстраивать иерархию анимаций, останавливать и продолжать их разом, менять скорость воспроизведения
  • Нет привычного графа сцены как в других 2д движках. Для обработки зависимостей используется dependency graph. У объектов есть множество атрибутов (координата, цвет, шейдер, ...) — можно задавать зависимости между ними, например, привязать поворот одного спрайта к координате Х другого. Движок производит рассчет только изменившихся атрибутов, что по идее уменьшает затраты на обновление. По факту это похоже на нодовую архитектуру Maya, Nuke, материалов из UE — привязывай что хочешь к чему хочешь, лишь бы тип совпадал. Есть специальный ScriptNode которому можно добавлять свои атрибуты и задавать коллбек на их обработку
  • Несколько классов для работы с тайлмапами. Hex, diamond, rectangular сетки, поиск пути
  • Есть поддержка 3д — то есть все трансформы и объекты по сути своей 3д


Минусы и недоработки:
  • Пробелы в документации, примеры часто нерабочие. Немногочисленное сообщество
  • Для сборки используется Cmake. Здесь нет одной кнопки «собрать под ...» как в Юнити или Короне, часто приходится бороться с ошибками компилятора и линкера
  • Lua api не полный. Например, часто нет баланса между сеттерами и геттерами (сеттеров больше). Поэтому полезно разбираться в написании биндингов и расширять их при необходимости
  • Нет редактора
  • Встроенный звуковой движок UNTZ очень примитивный. Есть биндинги к fmod, но скорее всего они сильно устарели, т.к. ими вроде как никто не пользуется
  • Видимо, с монетизацией фреймворка у разработчиков не сложилось — делать платным и закрытым его категорически не хочется, а MOAI Cloud не оправдал себя. На всякую ерунду типа приведения в порядок документации и примеров у Zipline Games нет ни времени, ни мотивации, все в основном делается сообществом. Это не значит что MOAI SDK мертв, нет, Zipline Games используют MOAI для внутренних проектов, развитие продолжается (вот совсем недавно появилась поддержка векторных примитивов через libtess, в процессе разработки собственный движок для обработки простых столкновений, чтобы не тащить целиком box2d или chipmunk)


Установка (Mac OS X)


Опишу установку только на OS X, т.к. не имею под рукой windows системы.

Клонируем официальный репозиторий:
git clone https://github.com/moai/moai-dev.git

Запускаем скрипт, который соберет хост под нашу систему:
cd moai-dev
bin/build-osx-sdl.sh 

Надеюсь все пройдет без ошибок, тогда исполняемый файл станет доступен в release/osx/host-sdl/bin/moai, сделаем на него линк (~/bin у меня добавлен в PATH):
ln -s /Users/vavius/moai-dev/release/osx/host-sdl/bin/moai ~/bin/moai

Запускаем пример, убеждаемся что все работает:
cd samples/hello-moai
moai

Видим крутящуюся фиговину и приветствующий текст:


Пишем на Lua


Начнем с минимального примера — нарисуем квадратный спрайт по центру экрана:

Код:
-- 1
MOAISim.openWindow ( "sample", 600, 240 )

local viewport = MOAIViewport.new ()
viewport:setSize ( 600, 240 )
viewport:setScale ( 600, 240 )

-- 2
local layer = MOAILayer.new ()
layer:setViewport ( viewport )

-- 3
local renderTable = { layer }
MOAIGfxDevice.getFrameBuffer ():setRenderTable ( renderTable )

-- 4
local deck = MOAIGfxQuad2D.new ()
deck:setTexture ( "moai.png" )
deck:setRect ( -64, -64, 64, 64 )

-- 5
local prop = MOAIProp.new ()
prop:setDeck ( deck )

layer:insertProp ( prop )

Описание происходящего по пунктам:
  1. Создаем окно и вьюпорт. Вьюпорт позволяет работать в логических координатах, не привязываясь к пикселям
  2. Слой — это контейнер для пропов (а пропами называется все что может быть нарисовано). Слою можно задать камеру, при этом будут рендериться только те пропы, которые попадают в текущий вьюпорт. Слой отвественнен за сортировку (порядок рендеринга) — есть множество вариантов, по координате X, Y, Z по отдельности или по вектору, по приоритету (целое число), а также специальный вид ISO_SORT для изометрических тайловых игр. У каждого слоя есть контейнер MOAIPartition для оптимизаций пространственных запросов — хит теста и raycast'ов, внутри используется многоуровневая сетка
  3. Задаем рендер таблицу — список renderable объектов, которые будут отрисованы во фреймбуфер по порядку. Слой при рендере рисует все добавленные в него пропы. Таблица может содержать вложенные таблицы, что очень удобно для создания менеджера сцен
  4. Deck — объект, определяющий визуальную часть. Он хранит геометрию (в данном случае квад — два треугольника), UV координаты, ссылку на текстуру и шейдер
  5. Prop — собирательный образ объекта, который рисуется на экране. Это в принципе не обязательно один отдельный спрайт, а также может быть и 3д меш и тайловая карта в зависимости от установленной деки

Сразу бросается в глаза, что уж больно много строк нужно для казалось бы простой задачи нарисовать спрайт. Чаще всего это делается в одну строчку, а тут надо как минимум шесть. Поэтому, API предоставляемый движком не используется «как есть», а многие пишут свои обертки на Lua. В С++ части MOAI SDK отсутствуют многие привычные вещи такие как: кэш текстур, загрузка спрайтов из атласов, менеджер сцен и переходов между ними, всякие кнопки и другие гуи элементы. Все это предлагается реализовать на Lua. На общей производительности это врядли сильно скажется, поскольку большинство операций выполняются только при ининциализации сцены. Конечно, надо подходить с умом к написанию кода, не плодить лишних таблиц, использовать старые объекты, кэшировать что можно.

Полная свобода


Допустим, не хотим квадратный квад, а хотим трапецию, вместо setRect используем setQuad:
local deck = MOAIGfxQuad2D.new ()
deck:setTexture ( "moai.png" )
deck:setQuad ( 
    -64, 64, 
    64, 64, 
    100, -64,
    -100, -64 )  -- координаты вершин с левого верхнего угла по часовой стрелке

Вот что выйдет:


Замостим фон целиком нашей картинкой. Можно использовать GL_REPEAT на текстуре, но он работает только для размеров кратных степеням двойки, и картинку с атласа не получится использовать. Поэтому воспользуемся классом MOAIGrid:
local deck = MOAIGfxQuad2D.new ()
deck:setTexture ( "moai.png" )
deck:setRect ( -0.5, -0.5, 0.5, 0.5 )

local grid = MOAIGrid.new ()
grid:initRectGrid ( 1, 1, 128, 128 )
grid:fill ( 1 )
grid:setRepeat ( true, true )

local prop = MOAIProp.new ()
prop:setDeck ( deck )
prop:setGrid ( grid )

layer:insertProp ( prop )

MOAIGrid нужен для работы с тайлами, здесь мы инициализируем карту из одного тайла размером 128x128. Затем ставим ему индекс 1 методом fill. Некоторые типы дек поддерживают индексацию, например MOAIGfxQuadDeck2D позволяет задавать много пар вершинных и UV координат для одной текстуры, что используется для представления спрайтового атласа. В данном случае в нашей деке есть только один единственный индекс 1. Включаем повторение и указываем пропу, что следует использовать сетку для рендеринга.


На этом этапе очень просто сделать анимированный прокручивающийся фон. Добавляем одну строчку в конец:
prop:moveLoc ( -128, 0, 0, 4, MOAIEaseType.LINEAR ):setMode ( MOAITimer.LOOP )

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


Анимация и Action tree


Перемещение и вращение объектов, а также установка режимов проигрывания анимаций:
local move = prop:moveLoc ( 200, 0, 0, 2 )                      -- двигаем
local rot = prop:moveRot ( 0, 0, 360, 2, MOAIEaseType.LINEAR )  -- крутим вокруг оси Z

move:setMode ( MOAITimer.PING_PONG )    -- вперед-назад
rot:setMode ( MOAITimer.LOOP )          -- цикл


По умолчанию анимации добавляются к корню action tree. Но можно их группировать:
local action = MOAIAction.new ()
action:addChild ( move )
action:addChild ( rot )

action:start ()
action:throttle ( 0.5 )

Теперь мы можем останавливать и запускать сразу обе анимации, а с помощью throttle задавать скорость воспроизведения.

Последовательность действий реализуется через Lua корутины. MOAI предоставляет класс MOAICoroutine, унаследованный от MOAIAction, что позволяет добавлять корутины в action tree. Функция blockOnAction вызывает yield пока экшн не закончится.

Двигаем картинку вправо-влево, а при достижении крайних точек делаем один полный оборот:
local function func ()
    local distance = 200
    while true do
        local action1 = prop:moveLoc ( distance, 0, 0, 2 )
        MOAICoroutine.blockOnAction ( action1 )

        local action2 = prop:moveRot ( 0, 0, 360, 2 )
        MOAICoroutine.blockOnAction ( action2 )

        distance = -distance
    end
end

local thread = MOAICoroutine.new ()
thread:run ( func )




Заключение


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

Я бы не советовал использовать MOAI SDK в продакшене без знания С++, нюансов сборки под выбранную платформу и готовности что-то менять внутри. Практически каждый, кто использует MOAI SDK имеет свой форк, который немного отличается от главной ветки. Исторически это связано с тем, что у Zipline Games не было времени мержить пулл-реквесты. Однако, сейчас некоторые члены сообщества получили доступ к официальному репозиторию и разработка пошла бодрее.

Благодаря открытости мы смогли реализовать live reload кода и ресурсов прямо на девайс. Сейчас я потихоньку пилю редактор, по образу и подобию Unity3d. Хотя дела пошли медленнее после заработавшего live reload'a — его хватает с головой для невероятного ускорения разработки. Интерфейсы собираем в векторном редакторе и экпортим сразу в код (декларативного вида, вот пример: gist.github.com/Vavius/9868572). Конечно, все это можно было приделать к любому движку, к cocos2d-x вообще без проблем, к Короне посложней, но тоже реально. Вобщем, для 2д игр пересели мы с кокоса на MOAI и нисколько не жалеем, здесь как-то все более по-взрослому, гибче и круче + код чистый и красивый.

Ссылки


getmoai.com — официальный сайт
getmoai.com/docs/annotated.html — доки
moaiwebsite.github.io — неофициальный сайт, пилится сообществом. Когда-нибудь станет новым лицом
github.com/makotok/Hanappe — высокоуровневый Lua-фреймворк в ООП-стиле. Из всех подобных решений только этот сейчас развивается и поддерживается.

Update:
moaifiddle.com/Q09BJWGMW6/3 — js версия движка. Теперь можно поиграться с движком без установки!
Поделиться публикацией
Похожие публикации
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 19
  • +1
    Судя по примерам — очень круто, на порядок выше Corona SDK, хотя и порог входа судя по всему много выше.
    • 0
      Все так. Для опытных разработчиков и студий, новичкам будет туго. Double Fine для своей последней Broken Age использовали MOAI (на кикстартере игра собрала $3.3М).

      После тюнинга инструментария скорость разработки не уступает Короне, и даже выше из-за того что не приходится искать костыли для исправления багов в ядре. Я помню где-то неделю разбирался с основами, потом нашел замечательный фреймворк flower и все сразу стало много проще.
    • 0
      > единственным конкурентом может быть только Unity3d
      Moai не плох, но блин, он ведь даже до своих конкурентов не всегда дотягивает, особенно по «простоте». 2Д движков с редакторами ведь множество и у Moai — далеко не самый удобный, как, в прочем и у Unity для 2d. Это если игнорировать факт что для всех популярных движков, можно использовать внешние редакторы.
      • 0
        По простоте любой конкурент уделывает MOAI на порядок.

        А все эти внешние редакторы как-то так себе. Мало кастомизации под логику игры. Это я про редакторы уровней. Редакторы должны быть как в 3Д движках, интегрированные в сам движок прямо с логикой разрабатываемой игры. Есть ли такое пригодное для 2д?

        Для сборки интерфейсов я пришел к тому, что удобнее писать скрипты для графических пакетов, экспортирующие координаты. Далее либо кодогенератор, либо парсер в рантайме.
      • 0
        В плане простоты, удобства, уровня вхождения, кроссплатформенности, чем плох libGDX?
        Здесь получается примерно, кому какие фломастеры больше нравятся.
        • 0
          libGDX очень крут. Думаю, что к нему в основном приходят андроид разработчики, когда хотят выпускать проложки и под iOS. А я начинал с другой стороны, мне C++ ближе чем java. Именно ситуация с фломастерами.
        • 0
          Растянула спрайт в трапеции мягко говоря не очень. Или я что-то не понял и так задумано?
          • 0
            Отсутствие должного внимания к MOAI SDK, очевидно

            и объясняется простым требованием при публикации в AppStore:
            An Application may not itself install or launch other executable code by any means, including the use of a plug-in architecture, calling other frameworks, other APIs or otherwise. No interpreted code may be downloaded or used in an Application except for code that is interpreted and run by Apple's Documented APIs and built-in interpreter.

            • 0
              Не скачиваем дополнительный код с сервера, а используем только то, что было включено в бандл. Встроенный интерпретатор можно использовать.
              • 0
                Под встроенным интрепретатором подразумевается не встроенный в ваше приложение(иначе упоминание интрепретатора в правила вообще не имеет смысла), а встроенное в API. Например, в API встроен интрепретатор JS — его использование не запрещено.
                При этом понятно, что фильтр проходит целая куча приложений нарушающих это правило.
                Но это похоже на русскую рулетку. Большинство разработчиков русскую рулетку не любят и предпочитают следовать правилам.
                • +2
                  У вас видимо старая формулировка. У меня сейчас в пункте 3.3.2 из iOS Developer Program License Agreement вот так написано:
                  An Application may not download or install executable code. Interpreted code may only be used in an Application if all scripts, code and interpreters are packaged in the Application and not downloaded. The only exception to the foregoing is scripts and code downloaded and run by Apple's built- in WebKit framework, provided that such scripts and code do not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store.

                  В данной формулировке было бы странно полагать, что речь идет только об интерпретаторах из официального API.
                  • 0
                    Спасибо что поправили! Вот эта новость действительно радует. :)
            • 0
              а единственным конкурентом может быть только Unity3d из-за своего редактора.

              что насчет
              http://www.godotengine.org/wp/?
              • 0
                Слышал, но пока еще не пробовал. Очень интересный, да.
                На рынке 3Д движков есть из чего выбирать, помимо Юнити теперь и UE4, и CryEngine стали доступны широким массам. А еще есть Project Anarchy, тоже ААА, хоть и сыроват слегка.
                • 0
                  ну годот — далеко не ААА, там можно почти что на коленках лепить достаточно клевые игрушки.

                  а так да, выбор широкий. Команду бы набрать хорошую…
              • 0
                Несколько раз смотрел, но хочется делать игры, а не допиливать фреймворк(
                • 0
                  Да и у меня так было. После 2, 5, 10+ донных игр (ну а что поделаешь, 99.8% всех игр на дне) уже как-то остужается пыл и пилить фреймворки становится тоже интересно )
                • 0
                  Еще книга есть по движку, случайно недавно нашел :)
                  Скрытый текст
                  image
                  • 0
                    Что-то я пропустил эту замечательную статью. От себя пару копеек — для моаи писал фреймворк для создания динамических лейаутов.
                    github.com/Nepherhotep/terevaka
                    github.com/Nepherhotep/terevaka-ui-builder

                    На самом деле — это здорово упрощает создание графики, т.к. имея нарезку спрайтов, можно очень быстро создать лэйаут, адаптируемый к размеру экрана (используя policy привязки к центру, левомой или правой стороне экрана, либо пропорционально от ширины экрана).

                    Фреймворк заточен для landscape приложений, т.к. создавался под конкретную игру, но несложно адаптировать и для portrait приложений.

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