How To Make a Simple Game with Moai
This is a tutorial for beginner Moak SDK developers, you’ll learn how to create a new animal-feeding game for iOS from scratch. With Moai, you don’t need to fear being locked in to one platform — you can let everyone enjoy the fruits of your labors! By .
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How To Make a Simple Game with Moai
55 mins
- What is Moai?
- Getting Started
- Ready Player One — Getting Your Project Ready
- What’s Invisible and Smells Like Carrots? — Adding Sprites to the Game
- Fast Food — Setting Object Position and Movement
- Cats and Dogs and Bunnies, Oh My! — Adding Characters to the Game
- Feeding the Hordes — Adding Interaction to the Game
- Picky Eaters — Adding Drag and Drop Logic to the Game
- Meow, Bark, Squeak — Adding Sound to your Game
- You’ve Been Fed, Now Shoo! – Removing Sprites
- Won’t These Animals Ever Stop Eating? — Displaying the Score On-Screen
- Taking this Dog and Pony Show On the Road — The Mobile Version
- Where to Go From Here?
What’s Invisible and Smells Like Carrots? — Adding Sprites to the Game
You’ll need to add a few things to liven up this game. Start by adding some sprites.
Add this code to the bottom of main.lua:
local quad = MOAIGfxQuad2D.new ()
quad:setTexture ( "gfx/carrot.png" )
quad:setRect ( -84/2, -98/2, 84/2, 98/2 )
local prop = MOAIProp2D.new()
prop:setDeck ( quad )
layer:insertProp ( prop )
A “quad” is a rectangle — hence the name “quad” — that can hold a texture image. Think of it as an abstract object, much like a class definition.
A “prop” is what might be called a “sprite” in another framework. It is something that is displayed on-screen. If a quad is comparable to a class, then a prop is like a practical, usable instance of that class.
With this setup, you could load a texture into a quad, and then use it multiple times in many props. You can read more about the distinction between the two on the Moai wiki.
Run your project, and now you are rewarded with a mighty-looking carrot in the center of the screen!

Note that the default position of your graphics is smack dab in the center of the screen.
Coordinates in a typical Moai application are a bit different than what you’re used to in Cocos2D or Corona SDK applications. The (0, 0) point is at the center of the window! Positive X values are to the right side and negative to the left; positive Y values are up and negative on the bottom, as shown in the image below:
You’re likely thinking “That’s a heck of a lot of code to display a single sprite! Do I really need to type all that code for every graphic on my screen?”
It probably makes sense to set up a reusable function that will do all the graphics housekeeping for your game. The average game will probably have many sprites, so you’ll want to make creating a sprite nice and easy to save you coding time.
Replace the last bit of code you pasted in (from “local quad = “MOAIGfxQuad2D.new ()” down to “layer:insertProp ( prop )”) with the following:
----------------------------------------------------------------
-- Textures and sprites creation
----------------------------------------------------------------
local textureCache = {}
local function textureFromCache ( name, width, height )
if textureCache [ name ] == nil then
textureCache[name] = MOAIGfxQuad2D.new ()
textureCache[name]:setTexture ( name )
textureCache[name]:setRect ( -width/2, -height/2, width/2, height/2 )
end
return textureCache [ name ]
end
local function newSprite ( filename, width, height )
if width == nil or height == nil then
-- read width/height from the image
local img = MOAIImage.new ()
img:load ( filename )
width, height = img:getSize ()
img = nil
end
local gfxQuad = textureFromCache ( filename, width, height )
local prop = MOAIProp2D.new ()
prop:setDeck ( gfxQuad )
prop.filename = filename
return prop
end
In the code above, newSprite() takes a filename as an argument with optional width and height of the image. If the width and height aren’t passed in as arguments, the function will figure out the image dimensions once it’s been loaded.
The textureCache object is used by newSprite() to represent the graphics object to display on-screen. As well, setDeck is a Lua hashtable, comparable to an NSDictionary that will hold each texture as it’s loaded. Since textures will be reused a lot, this will help speed things up.
Finally, newSprite sets the filename property of the object and returns the newly created sprite to the caller for later reference.
Now you’re able create a sprite with a single line of code! However, you’ll still need three lines total to add and position the sprite on the layer.
Paste in these lines at the bottom of main.lua:
local sp1 = newSprite ( "gfx/carrot.png", 84, 98 ) -- here we supply width and height
sp1:setLoc( -100, -230 ) -- set location - we move the sprite to the left and down
layer:insertProp ( sp1 )
local sp2 = newSprite ( "gfx/bone.png" ) -- no width and height supplied, it will be read from the file size
sp2:setLoc ( 100, -230 ) -- move to the right and down
layer:insertProp ( sp2 )
Run your app, and now your screen should look like the image below:
You could of course have a super-function that (in addition to loading the image) also sets the position and adds it to a layer. However, in this tutorial, you’ll keep things simple and depend on newSprite() to only load the image and handle the caching.
Now that you have an easy way to throw images up on the screen, it’s time to get those images moving around!
First, remove those six lines that you just pasted in main.lua; you’ll be adding a more generic spawnFoodObject() method in the next section that will make your life a little easier.
Fast Food — Setting Object Position and Movement
You’ve already seen the setLoc method used to set an object’s position. Similarly, there’s a setRot method that takes a rotation angle and a setColor method to change the object’s color and alpha (transparency).
To perform animation, such as moving/rotating/fading out a sprite over a period of time, there is a set of similarly-named methods:
- moveLoc ( deltaX, deltaY, time ) or seekLoc ( X, Y, time ) – move sprite to a different location
- moveRot ( deltaAngle, time) or seekRot ( angle, time ) – rotate
- moveColor ( deltaRed, deltaGreen, deltaBlue, deltaAlpha, time ) or seekColor ( Red, Green, Blue, Alpha, time ) – change the tint of the sprite
moveLoc, moveRot and moveColor change the respective property by adding the delta to the current value, while seekLoc, seekRot and seekColor create transition from the current value to the destination value.
In other words, the “move” methods use the current value as the starting point, and alter the object’s position according to the delta value you passed in. In contrast, the “seek” methods are absolute functions, where the object’s position is based on the passed-in position value alone.
You’ll need one more helper function before getting to animation.
Paste the following code at the bottom of main.lua:
----------------------------------------------------------------
-- various utilities
----------------------------------------------------------------
local rand = math.random
math.randomseed ( os.time ())
-- The multiple rand below is due to OSX/BSD problem with rand implementation
-- http://lua-users.org/lists/lua-l/2007-03/msg00564.html
rand (); rand (); rand ();
local function randomArrayElement ( array )
return array [ rand ( #array ) ]
end
randomArrayElement() returns a random element of an array. This saves some typing since most games have plenty of random events. In this game, you’ll be using it to spawn random types of food.
Next, paste in this code at the bottom of the file:
local function spawnFoodObject ()
local foodGfxs = {
"bone.png",
"carrot.png",
"catfood.png",
"dogfood.png",
"2catcans.png",
}
local foodName = randomArrayElement ( foodGfxs )
local foodObject = newSprite ( "gfx/" .. foodName )
foodObject:setPriority(99) -- food objects should be "on top" of everything else
foodObject:setLoc(-520, -230) -- initial position, outside of the screen
local anim = foodObject:moveLoc ( STAGE_WIDTH*1.2, 0, 12, MOAIEaseType.LINEAR )
anim:setListener ( MOAIAction.EVENT_STOP, function ()
local x, y = foodObject:getLoc()
if x > STAGE_WIDTH/2 then
layer:removeProp(foodObject)
foodObject = nil
end
end )
layer:insertProp ( foodObject )
end
spawnFoodObject ()
Take a run through the comments below, which explain the code block you just added.
You should recognize the calls to randomArrayElement and newSprite here. The randomly-chosen sprite is created and positioned at (-520, -230), which is just outside of the window to the left.
The animation is added next by calling moveLoc on the sprite. The first argument is the deltaX, which is how much the X position should change. STAGE_WIDTH * 1.2 is a safe value which allows the sprite to go from the outside of the left side of the screen to the outside of the right side of the screen.
The second argument is deltaY and is set to 0. This means the sprite only moves horizontally since there’s no change to the Y value. The third argument is the time for the movement, and in this case, the movement should take 12 seconds.
The fourth argument requires a bit more attention. It sets the animation curve to linear, which makes the animation play at a constant speed. If this argument wasn’t provided, the default would be an ease-in ease-out curve, where you would see slow movement at the beginning, then fast, then slow again at the very end. In this particular game, it looks best if the food moves at a constant speed, but it may vary for your game.
setListener allows you to define what should happen when the animation stops. This happens either when the animation time runs out, or when you manually call anim:stop(). At this point the sprite should be off-screen so you remove it from the layer and set its reference to nil so it can be garbage collected.
Run your project, and you should see one of five random food items move across the screen, as in the screenshot below:
That’s great, but you really want spawnFoodObject() to run over and over again to provide an endless parade of food across the bottom of your screen.
Replace the call to spawnFoodObject() in main.lua with the following code:
----------------------------------------------------------------
-- Looped timer factory function
----------------------------------------------------------------
local function newLoopingTimer ( spanTime, callbackFunction, fireRightAway )
local timer = MOAITimer.new ()
timer:setSpan ( spanTime )
timer:setMode ( MOAITimer.LOOP )
timer:setListener ( MOAITimer.EVENT_TIMER_LOOP, callbackFunction )
timer:start ()
if ( fireRightAway ) then
callbackFunction ()
end
return timer
end
local foodSpawnTimer = newLoopingTimer ( 1.5, spawnFoodObject, true)
This is another convenient helper function that makes it simple to create looped timers. That’s good — you’ll be using a lot of those! newLoopingTimer() itself takes a function as an argument, so passing in spawnFoodObject() ensures it will be called as often as you need it to.
Run your project, and you should see an endless moving line of treats, like so:
Is it weird that this makes me hungry? ;]