Alright, the title might be a bit over the top… what we are about to do is look at some of the most popular 2D game engines powered by Lua. First there will be a matrix of features, to give you an “at a glance” view of what each engine offers. Then we will follow up with a simple Hello World example for each, so you can see what the code would look like. Hopefully this will help you decide which engine is right for you.
Engine Features Matrix
* Note, I gave iTunes link only, although many of those games are also available on Google Play.
Now we are going to look at a simple Hello World app written with each suite. I do not pretend mastery of any of these suites, or Lua in general, so take the code for what it’s worth. If you wish to submit a better rendition, please do so!
In this sample we are going to create a window at a resolution of 1280x800, then we are going to start a background song looping ( Richard Wagners – Ride of the Valkyrie taken from here ). Then we are going to create a Hello World text/graphic centered to the screen, and position it where ever the user clicks/touches. Some files handle window creation in a different file, some handle it in a single file. That is why some versions have two lua files, while others have only one.
Corona SDK Hello World
config.lua
-- config.lua
application =
{
content =
{
width = 1280,
height = 800,
scale = "letterbox"
},
}
main.lua
-- HelloWorld sample
-- Load audio file
local song = audio.loadSound("../../Media/Ride_of_the_Valkyries.mp3")
-- set volume to 50%
audio.setVolume(0.5)
-- play audio file, looping forever
audio.play(song,{ channel=1,loops=-1})
-- create text to display on screen in 72point font
local helloText = display.newText("Hello World!",0,0,native.systemFont,72)
-- center to screen
helloText.x = display.contentWidth/2
helloText.y = display.contentHeight/2
-- red
helloText:setTextColor(255,0,0)
-- function to handle touch event, move helloText to the touch location
function onTouchHandler(event)
helloText.x = event.x
helloText.y = event.y
end
-- register touch function to respond to global touch events
Runtime:addEventListener("touch",onTouchHandler)
Gideros
main.lua
-- Helloworld sample
-- setup our window to our 1280x800 resolution
application:setLogicalDimensions(1280,800)
application:setOrientation(Application.LANDSCAPE_LEFT)
-- Load song, cannot use relative path to parent directory since file needs to be added to project
local song = Sound.new("Ride_of_the_Valkyries.mp3")
-- play audio file, looping forever
local soundChannel = song:play(0,math.huge)
-- Set song volume to 50%, not set globally
soundChannel:setVolume(0.5)
-- need to load a ttf font, size cannot specify character size in TextField
local font = TTFont.new("arial.ttf",72,false)
-- create text to display on screen
local helloText = TextField.new(font,"Hello World!")
-- center to screen
helloText:setPosition(
application:getLogicalWidth()/2 - helloText:getWidth()/2,
application:getLogicalHeight()/2 + helloText:getHeight()/2)
-- set text to red, color is hex encoding
helloText:setTextColor(0xff0000)
-- display text
stage:addChild(helloText)
-- function to handle touch event, move helloText to the touch location
function onTouchHandler(event)
helloText:setPosition(event.x - helloText:getWidth()/2,event.y + helloText:getHeight()/2)
end
-- register touch function to respond to global touch events
stage:addEventListener(Event.TOUCHES_BEGIN,onTouchHandler)
-- The above doesn't work in the simulator, so handle mouse too
stage:addEventListener(Event.MOUSE_DOWN,onTouchHandler)
LÖVE
love.conf
function love.conf(t)
t.screen.width = 1280
t.screen.height = 800
end
main.lua
-- love2d works slightly different, expecting users to implement methods that will be called within the game loop
-- such as love.draw() and love.update()
-- create a 72 point font using the system default
font = love.graphics.newFont(72)
-- set the font active
love.graphics.setFont(font)
-- set red as the active color
love.graphics.setColor(255,0,0,255)
-- load audio file
local song = love.audio.newSource("Ride_of_the_Valkyries.ogg")
-- we want to loop, we want to loop, we want to loop, we want t^Z
song:setLooping(true)
-- set volume to 50%
love.audio.setVolume(0.5)
-- play song
love.audio.play(song)
-- create a variable for print coordinates to update on touch, default to screen center
-- LOVE does not have a positionable text object, so we call print each frame
local x = love.graphics.getWidth()/2
local y = love.graphics.getHeight()/2
local stringWidth = font:getWidth("Hello World!")
local stringHeight = font:getHeight("Hello World!")
-- This function is called once per frame to draw the screen
function love.draw()
love.graphics.print("Hello World!",x - stringWidth/2,y-stringHeight/2)
end
-- called on click, move our print x,y to the click location
-- no touch handler because LOVE is desktop only
function love.mousepressed(mouse_x,mouse_y,button)
x = mouse_x
y = mouse_y
end
Moai
main.lua
-- create the window, viewport and layer
MOAISim.openWindow("Window", 1280, 800)
local viewport = MOAIViewport.new()
viewport:setSize(1280,800)
viewport:setScale(1280,800)
local layer = MOAILayer2D.new()
layer:setViewport(viewport)
-- Let Moai know we want this layer rendered
MOAIRenderMgr.pushRenderPass(layer)
-- Initialize the audio system
MOAIUntzSystem.initialize()
-- set volume to 50%
MOAIUntzSystem.setVolume(0.5)
-- load the song
song1 = MOAIUntzSound.new()
song1:load("../Media/Ride_of_the_Valkyries.ogg")
-- play audio file, looping forever
song1:setLooping(true)
song1:play()
-- save memory by only rendering the chars we need
chars = 'Helo Wrd!'
-- create a font
local font = MOAIFont.new()
font:loadFromTTF('../Media/arial.ttf',chars,72)
-- create and position text centered
local helloText = MOAITextBox.new()
helloText:setString('Hello World!')
helloText:setFont(font)
helloText:setYFlip(true)
helloText:setRect(-640,-400,640,400)
helloText:setAlignment(MOAITextBox.CENTER_JUSTIFY,MOAITextBox.CENTER_JUSTIFY)
layer:insertProp(helloText)
-- handle mouse/touch click events
function handleClickOrTouch(x,y)
helloText:setLoc(layer:wndToWorld(x,y))
end
if MOAIInputMgr.device.pointer then
-- Mouse based device
MOAIInputMgr.device.mouseLeft:setCallback(
function(isButtonDown)
if(isButtonDown) then
handleClickOrTouch(MOAIInputMgr.device.pointer:getLoc())
end
end
)
else
-- Touch based device
MOAIInputMgr.device.touch:setCallback(
function(eventType,idx, x, y, tapCount)
if eventType == MOAITouchSenser.TOUCH_DOWN then
handleClickOrTouch(x,y)
end
end
)
end
My Opinions
First off, take these with a grain of salt, these are just my initial impressions and nothing more. Obviously it is all very subjective. It is also stupid to say X is best, they are all good libraries, each with their own strengths and weaknesses. I think that is perhaps the greatest surprise, not one of these four options is bad.
Love: Not a big fan of the abstraction and it forces a design on you, but this isn’t necessarily a bad thing, especially for a beginner. Good for beginners, so-so to slight levels of documentation but absolutely wonderful reference materials. Only library in this group with no mobile support, which is a big deal. Open source and free, targeted to hobbyist. Few ( none? ) commercial games. All told, it reminded me a lot of the Python based PyGame, which is frankly a great beginners library. Also the name “Love” proved a gigantic handicap, as it made Googling for anything beyond the Love2D website very difficult. This is the downside to using a very generic name for your library ( cough… GamePlay, I’m looking at you! ). The generic name really did prove to be a pain in the butt at times. Love is certainly a good library, but probably not for professional use, at least, as is.
Corona: Most polished of the four. Best documentation, good API. Only library with published books available and good tooling support. Also most expensive and closed. If it isn’t part of Corona, you are hosed. Have to pay more for native access. Great developer backing, lots of successful companies using Corona. Corona is certainly a great library, although thanks to the price tag, it wont appropriate for all developers. The lack of freedom ( no source, paying for native access ) are definitely the biggest drawbacks.
Gideros: Ok-good documentation, good reference but other material is a bit too scattered. IDE is a HUGE boon for newer developers, especially with auto-completion. That said, the IDE got a bit flaky at times too. API itself a bit less intuitive ( to me ). Licensing terms reasonable ( better than Corona, worse than Love and Moai ), same for price. Good choice for beginner who wants to support mobile, lack of major published games a bit of a deterrent for professional developers, as is the lack of source code.
Moai: Moai is certainly the most difficult of the four, and the documentation is in heavy need of updating. The reference itself is actually very good, where it exists. In some cases there is none and in others, it is lacking or out-dated. The developers are aware and this is a priority for them to fix. On the other hand, Moai is also the most flexible by a mile. The code ( as you can see from the example above ), is a bit more verbose, but that is because the library makes less decisions for you. This is a double edged sword of flexibility vs ease, and Moai slants heavily towards flexibility. Supports the most targets of all the libraries, has complete source code, and more importantly, the source code is very well written and very easy to read. Like Corona, there are some very good shipped games.
Final verdict:
For a commercial product for iOS/Android, I would select Moai. The API is a natural fit to my coding style ( I prefer flexibility over accessibility for time critical code ) and the C++ source code is a great boon to me, but to a non-C++ programmer, this would obviously be less important. Also of course, the price is nice. Most importantly, the open nature means I know I will never encounter a problem that I can’t code my way out of, the biggest downside to Corona. If it wasn’t for the open source nature of Moai, I would probably go with Corona for the sake of it’s excellent documentation and clean API.
If I was just starting out, I would be torn between Gideros and LOVE. LOVE is certainly the most beginner friendly, but the turn-key all in one nature of Gideros… you literally install, load the studio, write some code and hit play… with full autocomplete code editing. This really is a huge deal! In it’s favour over LOVE is also the support for mobile platforms. That said, if the API isn’t to your liking, or you struggle with it, Love is easily the most accessible code wise. I will be looking a bit closer at Gideros in the future. Ran into a few annoyances during my brief exposure, like the inability to set anchor points for TextField values ( http://bugs.giderosmobile.com/issues/41 ), forcing me to wait for the feature to be added by someone else.
This isn’t to say Corona is bad, it obviously isn’t. It is polished, has the best documentation and a very solid/natural API. For me though, the lack of flexibility and access to source code provides outweigh it’s advantages. If the source isn’t a big deal to you, or you do not have access to C++ resources and are willing to pay 200$ a year or more, Corona is a very good option with a ton of developers using it. Also, Corona is the only option with a paid support option, which can be a huge advantage.
TL;DR verdict:
For a Pro developer: Go Moai, unless you have no in-house C++ talent, in which case, go Corona.
For a new developer: Go Gideros, especially if you want to do mobile development. If you don’t like it, Love is always a great option.
45c40d05-76d3-4523-ac10-f16eeee7da1c|10|5.0|96d5b379-7e1d-4dac-a6ba-1e50db561b04
Programming, Design, General
LUA, Moai, Engine, Design