Moai SDK 1.5 - cross-platform 2D game engine

Today I want to talk about one little-known game engine that we have been using for a year for cross-platform development of mobile games. For 2d, it completely suits us, and only Unity3d can be the only competitor because of its editor. The lack of proper attention to the MOAI SDK is obviously associated with a high entry threshold - the developers themselves (Zipline Games) position their product as “The mobile platform for pro game developers”, although once you figure out how to install and configure the environment, you can rivet games very quickly and easily Lua.
Why did I like MOAI so much:
- The engine is written in C ++, the game logic is written in Lua. There is support for luajit (on iOS only in interpreter mode)
- Open source. CPAL license - you must specify the logo, title, authors and a link to the MOAI on the loading screen or in credits
- Cross-platform: Windows, Mac OS X, Linux, iOS, Android, OUYA, and also there are experiments with html5 via emscripten. Development is supported on Windows, Mac OS X, and Linux. To build for iOS, you need a Mac. Theoretically, adding a new platform is not difficult - you need to write a “host” that will create an OpenGL context and will call input processing methods
- Lua API is very low level. This is both a plus and a minus. Effective development simply requires a higher-level Lua framework
- Inside, cool algorithms and technologies are used. Renderer with automatic batching (cocos2d seems to have learned this too). Action tree - everything that is periodically updated (animations, physics) is presented in the form of a tree, parent nodes pass the elapsed time (delta) to their children; this allows you to build a hierarchy of animations, stop and continue them at once, change the playback speed
- There is no familiar scene graph as in other 2D engines. A dependency graph is used to handle dependencies. Objects have many attributes (coordinate, color, shader, ...) - you can set dependencies between them, for example, attach the rotation of one sprite to the X coordinate of another. The engine calculates only the changed attributes, which in theory reduces the cost of updating. In fact, it is similar to the Maya, Nuke node architecture, materials from UE - bind what you want to what you want, if only the type matches. There is a special ScriptNode to which you can add your attributes and set a callback to process them
- Several classes for working with tilemaps. Hex, diamond, rectangular mesh, path finding
- There is support for 3D - that is, all transforms and objects are essentially 3D
Cons and flaws:
- Documentation gaps, examples often inoperative. Small community
- For assembly, Cmake is used. There is no one button "assemble under ..." as in Unity or Coron, often you have to deal with compiler and linker errors
- Lua api is not complete. For example, often there is no balance between setters and getters (there are more setters). Therefore, it is useful to understand the writing of binders and expand them if necessary
- No editor
- The integrated UNTZ sound engine is very primitive. There are binders for fmod, but most likely they are very outdated, because nobody seems to use them
- Apparently, the developers did not succeed with the monetization of the framework - I categorically do not want to make it paid and closed, and the MOAI Cloud did not justify itself. For all sorts of rubbish such as arranging documentation and examples, Zipline Games has neither time nor motivation, everything is mainly done by the community. This does not mean that the MOAI SDK is dead, no, Zipline Games use MOAI for internal projects, development continues (recently, support for vector primitives appeared through libtess, in the process of developing its own engine for processing simple collisions, so as not to drag the whole box2d or chipmunk)
Installation (Mac OS X)
I will describe the installation only on OS X, because I do not have the windows system on hand.
Clone the official repository:
git clone https://github.com/moai/moai-dev.git
Run the script that will collect the host for our system:
cd moai-dev
bin/build-osx-sdl.sh
I hope everything goes smoothly, then the executable file will be available in release / osx / host-sdl / bin / moai, let's make a link to it (I have added ~ / bin to PATH):
ln -s /Users/vavius/moai-dev/release/osx/host-sdl/bin/moai ~/bin/moai
We launch an example, we are convinced that everything works:
cd samples/hello-moai
moai
We see the spinning figovina and the welcoming text:

Writing on Lua
Let's start with a minimal example - draw a square sprite in the center of the screen:

The code:
-- 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 )
Description of what is happening on the items:
- Create a window and a viewport. Viewport allows you to work in logical coordinates without snapping to pixels
- A layer is a container for prop (and prop is called everything that can be drawn). The layer can be set to a camera, and only those props that fall into the current viewport will be rendered. The layer is responsible for sorting (rendering order) - there are many options, by X, Y, Z coordinate individually or by vector, by priority (integer), as well as a special ISO_SORT view for isometric tile games. Each layer has a MOAIPartition container for optimizing spatial queries - hit test and raycasts, a multi-level grid is used inside
- Set the render table - a list of renderable objects that will be drawn in the framebuffer in order. The render layer draws all the prop added to it. The table may contain nested tables, which is very convenient for creating a scene manager
- Deck - an object that defines the visual part. It stores geometry (in this case, a quad - two triangles), UV coordinates, a link to the texture and shader
- Prop - a collective image of the object that is drawn on the screen. This, in principle, is not necessarily one separate sprite, and there may also be a 3D mesh and a tile card, depending on the installed deck.
It is immediately evident that it hurts a lot of lines to draw a sprite for a seemingly simple task. Most often this is done in one line, but here you need at least six. Therefore, the API provided by the engine is not used "as is", and many write their wrappers in Lua. In the C ++ part of the MOAI SDK, many familiar things are missing such as: texture cache, loading sprites from atlases, scene and transition manager between them, all sorts of buttons and other gooey elements. All of this is proposed to be implemented on Lua. This is unlikely to significantly affect overall performance, since most operations are performed only when the scene is initialized. Of course, you need to approach code writing wisely, not produce extra tables, use old objects, and cache what you can.
Full freedom
Suppose we do not want a square quad, but want a trapezoid, instead of setRect we use setQuad:
local deck = MOAIGfxQuad2D.new ()
deck:setTexture ( "moai.png" )
deck:setQuad (
-64, 64,
64, 64,
100, -64,
-100, -64 ) -- координаты вершин с левого верхнего угла по часовой стрелке
Here's what comes out:

We cover the whole background with our picture. You can use GL_REPEAT on the texture, but it only works for sizes that are multiples of the powers of two, and you can’t use the picture from the atlas. Therefore, we will use the MOAIGrid class:
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 is needed for working with tiles, here we initialize a map from a single tile with a size of 128x128. Then we give it index 1 with the fill method. Some types of decks support indexing, for example MOAIGfxQuadDeck2D allows you to specify many pairs of vertex and UV coordinates for a single texture, which is used to represent a sprite atlas. In this case, in our deck there is only one single index 1. Turn on repetition and indicate the prop that the grid should be used for rendering.

At this point, it’s very easy to make an animated scrollable background. Add one line to the end:
prop:moveLoc ( -128, 0, 0, 4, MOAIEaseType.LINEAR ):setMode ( MOAITimer.LOOP )
Just in the loop we move our prop to the width of one tile, and the engine itself substitutes the missing pieces to bridge the entire screen:

Animation and Action tree
Moving and rotating objects, as well as setting animation playback modes:
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 ) -- цикл
By default, animations are added to the root of the action tree. But you can group them:
local action = MOAIAction.new ()
action:addChild ( move )
action:addChild ( rot )
action:start ()
action:throttle ( 0.5 )
Now we can stop and start both animations at once, and use throttle to set the playback speed.
The sequence of actions is implemented through Lua coroutines. MOAI provides the MOAICoroutine class inherited from MOAIAction, which allows you to add coroutines to the action tree. The blockOnAction function calls yield until the action ends.
We move the picture left and right, and when reaching the extreme points we make one complete revolution:
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 )

Conclusion
The article deals with very primitive examples - my goal was to show some aspects of the Lua API, namely its low level and modularity. The MOAI SDK tries not to make any decisions for us, does not force us to do everything in one generally accepted way, but leaves complete freedom. Of course, the community has already implemented several high-level wrappers on pure Lua, with texture caching, gui elements, scene manager, etc.
I would not recommend using the MOAI SDK in production without C ++ knowledge, the nuances of the assembly for the selected platform, and the willingness to change something inside. Almost everyone who uses the MOAI SDK has their fork, which is slightly different from the main branch. Historically, Zipline Games did not have time to pull pull requests. However, now some community members have gained access to the official repository and development has begun more vigorously.
Thanks to openness, we were able to implement live reload of code and resources directly to the device. Now I'm slowly sawing the editor, in the image of Unity3d. Although things went slower after the live reload started, it’s enough with its head to incredibly speed up development. We collect the interfaces in a vector editor and export directly into the code (declarative type, here is an example:gist.github.com/Vavius/9868572 ). Of course, all this could be attached to any engine, to cocos2d-x without any problems at all, to the Crown more complicated, but also real. In general, for 2d games we moved from coconut to MOAI and do not regret it at all, it’s somehow more and more adult, flexible and cooler + the code is clean and beautiful.
References
getmoai.com - the official site
getmoai.com/docs/annotated.html - docks
moaiwebsite.github.io - an unofficial site, sawn by the community. Someday it will be the new face of
github.com/makotok/Hanappe , a high-level Oua-style Lua framework. Of all such solutions, only this is now being developed and maintained.
Update:
moaifiddle.com/Q09BJWGMW6/3 - js version of the engine. Now you can play with the engine without installation!