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?
Feeding the Hordes — Adding Interaction to the Game
What’s a game without some player interaction? Things appear on-screen and move around, but you can’t actually feed the animals yet.
Time to add some input handlers!
Since Moai is meant to be cross-platform, there are very similar ways to handle both mouse and touch events from code. This is easy to do for the simple case — but of course there’s no way to get the mouse over event for a touch event, or a multi-touch event on a regular mouse! :]
Paste in the mouse/touch handling code at the bottom of main.lua:
----------------------------------------------------------------
-- Input (touches and mouse) handling
----------------------------------------------------------------
-- location of the mouse cursor (or touch point)
local mouseX, mouseY
-- this is to keep reference to what's being dragged
local currentlyTouchedProp
local function dragObject ( object, x, y )
print ("Dragging")
end
local function pointerCallback ( x, y )
-- this function is called when the touch is registered (before clickCallback)
-- or when the mouse cursor is moved
mouseX, mouseY = layer:wndToWorld ( x, y )
print ("mouse moved")
end
function clickCallback ( down )
-- this function is called when touch/click
-- is registered
print ("Click!")
end
These functions are generic and deal with (x,y) positions and some kind of “click”. It doesn’t really matter whether it’s a touch event or a mouse event — you only need one set of code to handle both! pointerCallback will keep track of the (x,y) position in general; clickCallback will handle touches and mouse clicks, and dragObject will be called as the player clicks and drags with either their mouse or their finger.
Now that your input handlers are in place, it’s time to register the callbacks.
Paste the following code at the bottom of main.lua:
-- Here we register callback functions for input - both mouse and touch
if MOAIInputMgr.device.pointer then
-- mouse input
MOAIInputMgr.device.pointer:setCallback ( pointerCallback )
MOAIInputMgr.device.mouseLeft:setCallback ( clickCallback )
else
-- touch input
MOAIInputMgr.device.touch:setCallback (
-- this is called on every touch event
function ( eventType, idx, x, y, tapCount )
pointerCallback ( x, y ) -- first set location of the touch
if eventType == MOAITouchSensor.TOUCH_DOWN then
clickCallback ( true )
elseif eventType == MOAITouchSensor.TOUCH_UP then
clickCallback ( false )
end
end
)
end
This bit of code checks the device type. On mouse devices, it simply sets a callback that is called when the pointer is moved and when the left mouse button is clicked. On touch devices, it sets a callback function for a touch event, calls pointerCallback() — just as for mouse movement — and then finally it calls clickCallback().
Note: If you develop a real cross-platform game, it’s a good idea to implement the UI best practices for each particular platform. For example, when using a mouse, people expect that interactive elements will change their appearance when the cursor is over them to indicate that they are interactive.
In iOS, you can use platform-specific code to handle multitouch input — if it makes sense in your game — and figure out a different way to implement the same behaviour in your game using a mouse.
Just because an SDK allows you to write one set of code for multiple platforms doesn’t mean that you shouldn’t implement platform-specific behaviors. However, that is out of scope for this particular tutorial.
Note: If you develop a real cross-platform game, it’s a good idea to implement the UI best practices for each particular platform. For example, when using a mouse, people expect that interactive elements will change their appearance when the cursor is over them to indicate that they are interactive.
In iOS, you can use platform-specific code to handle multitouch input — if it makes sense in your game — and figure out a different way to implement the same behaviour in your game using a mouse.
Just because an SDK allows you to write one set of code for multiple platforms doesn’t mean that you shouldn’t implement platform-specific behaviors. However, that is out of scope for this particular tutorial.
Run your app, play with the game, and check the Terminal to see if the print statements are being created on the console. You should see events like the following:
mouse moved mouse moved mouse moved Click! Click! mouse moved Click! Click! Click! mouse moved mouse moved
There’s something gratifying about seeing your app handle events — even if it’s just being dumped to Terminal! :]
Now you need to do something useful with your touch handlers, instead of just dumping them out to the console. The most important handler is the click function, as it handles all the touches and, depending on the context, further dispatches the event.
Replace all of clickCallback() in main.lua with the following code:
function clickCallback ( down )
-- this function is called when touch/click
-- is registered
local pick = partition:propForPoint ( mouseX, mouseY )
local phase
if down then
phase = "down"
else
phase = "up"
end
-- if the touch/click is currently "locked" on some object
-- this object should be treated as touched,
-- not something that's above it, for example
if currentlyTouchedProp then
pick = currentlyTouchedProp
end
event = {
target = pick,
x = mouseX,
y = mouseY,
phase = phase,
}
print ( phase, mouseX, mouseY )
if down then
if pick then
currentlyTouchedProp = pick
local x, y = currentlyTouchedProp:getLoc ()
-- we store the position of initial touch inside the object
-- so when it's dragged, it follows the finger/cursor smoothly
currentlyTouchedProp.holdX = x - mouseX
currentlyTouchedProp.holdY = y - mouseY
end
if pick and pick.onTouchDown then
pick.onTouchDown ( event )
return
end
else
currentlyTouchedProp = nil
if pick and pick.onTouchUp then
pick.onTouchUp ( event )
return
end
end
end
Players should be able to drag the food items to plates, so the click handler here needs to keep track of when the mouse button click/touch begins (touchDown) and when the button/touch ends (touchUp).
The object that is clicked on is stored in currentlyTouchedProp so it can be referenced later to check if it’s the correct food for the customer. As well, this reference is used so that the object can follow the mouse cursor or the finger on the screen.
In order to handle the drag event and move the sprites on the screen, replace the contents of dragObject() in main.lua with the following:
local function dragObject ( object, x, y )
object:setLoc ( x + object.holdX, y + object.holdY )
end
As an item is dragged to a new (x,y) position, you set its location to that same (x,y) position. holdX and holdY are just offsets, so it doesn’t matter which point you use to drag the object. For example, dragging the object having clicked on the center should work just as well as dragging by having clicked on the edge of the object.
Next, replace pointerCallback() in main.lua with the following code:
local function pointerCallback ( x, y )
-- this function is called when the touch is registered (before clickCallback)
-- or when the mouse cursor is moved
mouseX, mouseY = layer:wndToWorld ( x, y )
if currentlyTouchedProp and currentlyTouchedProp.isDragable then
dragObject(currentlyTouchedProp, mouseX, mouseY)
end
end
If there’s no dragging going on, you normally don’t care if the mouse moves around. currentlyTouchedProp will only need to be set if you’re in the middle of a drag event, at which point dragObject() will need to be called.
For a sprite to respond to touches and clicks, it needs an .isDragable = true attribute on the sprite. Food will be dragged to the animals, so you need to add .isDragable = true to the implementation of spawnFoodObject.
Update spawnFoodObject as below:
-- this line should already be here:
foodObject:setLoc(-520, -230) -- initial position, outside of the screen
-- add this one line below!
foodObject.isDragable = true
-- this line should already be here as well:
local anim = foodObject:moveLoc ( STAGE_WIDTH*1.2, 0, 12, MOAIEaseType.LINEAR )
Build and run to see how it looks so far. You can drag items, but after you drag them they continue to scroll:
Unfortunately, the food will continue to animate (conveyor belt style) even after you try to drag it! Hmm, the game is supposed to be challenging — but not that challenging! :]
Add the following lines to spawnFoodObject() in main.lua just before the final end that closes the function:
foodObject.onTouchDown = function ( ev )
anim:stop ()
end
Run your app!
Try to drag some food items around. Ahh, that’s better — the horizontal animation stops playing whenever you drag the food item around the screen. Try dragging and dropping the food to make sure that those handlers are working correctly! Your screen should look similar to the following screenshot: