In this tutorial series, you’ll learn how to create a game like Snake. The objective of the game is to grab the apples on screen to raise the score. Read on!
Where We Left Off. . .
Please be sure to check part 1 of the series to fully understand and prepare for this tutorial.
Step 1: Start Button Listeners
This function adds the necesary listeners to the TitleView buttons.
function startButtonListeners(action) if(action == 'add') then playBtn:addEventListener('tap', showGameView) creditsBtn:addEventListener('tap', showCredits) else playBtn:removeEventListener('tap', showGameView) creditsBtn:removeEventListener('tap', showCredits) end end
Step 2: Show Credits
The credits screen is shown when the user taps the about button, a tap listener is added to the credits view to remove it.
function showCredits:tap(e) playBtn.isVisible = false creditsBtn.isVisible = false creditsView = display.newImage('credits.png', display.contentCenterX - 110, display.contentHeight+40) transition.to(creditsView, {time = 300, y = display.contentHeight-20, onComplete = function() creditsView:addEventListener('tap', hideCredits) end}) end
Step 3: Hide Credits
When the credits screen is tapped, it’ll be tweened out of the stage and removed.
function hideCredits:tap(e) playBtn.isVisible = true creditsBtn.isVisible = true transition.to(creditsView, {time = 300, y = display.contentHeight+creditsView.height, onComplete = function() creditsView:removeEventListener('tap', hideCredits) display.remove(creditsView) creditsView = nil end}) end
Step 4: Show Game View
When the Start button is tapped, the title view is tweened and removed revealing the game view. There are many parts involved in this view so we’ll split them in the next steps.
function showGameView:tap(e) transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil end})
Step 5: Add Game Background
This code places the game background in the stage.
-- [Add GFX] -- Game Bg gameBg = display.newImage('gameBg.png')
Step 6: Add Game Pad
In order to move the snake in the screen we’ll need a game pad, this will take care of that. A tap listener will be added later to each arrow to handle the movement.
-- Pad up = display.newImage('up.png', 33.5, 219.5) left = display.newImage('left.png', 0, 252.5) down = display.newImage('down.png', 33.5, 286.5) right = display.newImage('right.png', 66.5, 252.5) up.name = 'up' down.name = 'down' left.name = 'left' right.name = 'right'
Step 7: Score Text
This line adds the score textfield:
-- Score Text score = display.newText('0', 74, 3, native.systemFontBold, 15) score:setTextColor(252, 202, 1)
Step 8: Head
Next we add the snake’s head. As mentioned in the previous tutorial, a hit area will be added on top of it and then both will be stored in a group.
-- Head headGFX = display.newImage('head.png', display.contentCenterX-0.3, display.contentCenterY-0.2) headHitArea = display.newLine(display.contentCenterX+6, display.contentCenterY-0.2, display.contentCenterX + 8, display.contentCenterY-0.2) headHitArea:setColor(0, 0, 0) headHitArea.width = 2 head = display.newGroup(headGFX, headHitArea) lastPart = head parts = display.newGroup()
Step 9: Initial Apple
The first apple is added by this code at a random position.
-- Add first apple local randomX = 25 + math.floor(math.random() * 402) local randomY = 25 + math.floor(math.random() * 258) local apple = display.newImage('apple.png', randomX, randomY) apples = display.newGroup(apple) gameListeners() end
Step 10: Game Listeners
This function adds the necessary listeners to start the game logic.
function gameListeners() up:addEventListener('tap', movePlayer) left:addEventListener('tap', movePlayer) down:addEventListener('tap', movePlayer) right:addEventListener('tap', movePlayer) end
Step 11: Move Player Function
The direction variable is changed by this function, this will make the snake go in the pressed direction.
function movePlayer(e) dir = e.target.name if(started == nil) then timerSrc = timer.performWithDelay(speed, update, 0) started = true end end
Step 12: Hit Test Objects
We’ll use an excellent and useful function for collision detection without physics, you can find the original example and source at the CoronaLabs Code Exchange website.
function hitTestObjects(obj1, obj2) local left = obj1.contentBounds.xMin <= obj2.contentBounds.xMin and obj1.contentBounds.xMax >= obj2.contentBounds.xMin local right = obj1.contentBounds.xMin >= obj2.contentBounds.xMin and obj1.contentBounds.xMin <= obj2.contentBounds.xMax local up = obj1.contentBounds.yMin <= obj2.contentBounds.yMin and obj1.contentBounds.yMax >= obj2.contentBounds.yMin local down = obj1.contentBounds.yMin >= obj2.contentBounds.yMin and obj1.contentBounds.yMin <= obj2.contentBounds.yMax return (left or right) and (up or down) end
Step 13: Get Head Last Position
We capture the snake’s head last position to update the parts position, each one replacing the x and y coordinates of the other.
function update() -- Capture head last position, this moves the first added piece local lastX = head.x local lastY = head.y local xPos = {} local yPos = {}
Step 14: Move Parts
This complements the behavior explained in the past step.
for i = 1, parts.numChildren do -- Capture parts position to move them xPos[i] = parts[i].x yPos[i] = parts[i].y -- Move Parts if(parts[i-1] == nil) then parts[i].x = lastX parts[i].y = lastY else parts[i].x = xPos[i-1] parts[i].y = yPos[i-1] end
Step 15: Check for Game Over
The game ends when the snake’s head touches another part of the snake. A sound is played as feedback.
-- Game over if head touches other part of snake if(hitTestObjects(headHitArea, parts[i])) then print(parts[i].name) if(parts[i].name == '1') then print('one') else timer.cancel(timerSrc) timerSrc = nil audio.play(gameOver) end end
Step 16: Move Head
These lines move the head according to the direction stablished by the movePlayer function created in step 11.
-- Move Head & Hit Area if(dir == 'up') then head.y = head.y - mConst headHitArea.x = headGFX.x headHitArea.y = headGFX.y - 7 elseif(dir == 'left') then head.x = head.x - mConst headHitArea.x = headGFX.x - 8 headHitArea.y = headGFX.y elseif(dir == 'down') then head.y = head.y + mConst headHitArea.x = headGFX.x headHitArea.y = headGFX.y + 8 elseif(dir == 'right') then head.x = head.x + mConst headHitArea.x = headGFX.x + 7 headHitArea.y = headGFX.y end
Step 17: Apple-Head Collision
Several actions occurr when the snake eats an apple we will go through them in the following steps.
First we remove the apple.
-- Detect apple-player collision for i = 1, apples.numChildren do if(hitTestObjects(head, apples[i])) then -- Remove Apple display.remove(apples[i])
Step 18: Update Score
Next we update the score textfield and play a sound to indicate that the apple has been eaten.
-- Add Score score.text = score.text + tostring(10) score:setReferencePoint(display.TopLeftReferencePoint) score.x = 74 audio.play(appleGrab)
Step 19: Add the Snake Part
Then a new part is added to the snake. Its position is calculated by the last part added (the first time is the head) and then they are added to a group.
-- Add snake body local part = display.newImage('bodyPart.png') if(dir == 'up') then part.x = lastPart.x part.y = lastPart.y + mConst elseif(dir == 'left') then part.x = lastPart.x + mConst part.y = lastPart.y elseif(dir == 'down') then part.x = lastPart.x part.y = lastPart.y - mConst elseif(dir == 'right') then part.x = lastPart.x - mConst part.y = lastPart.y end current = current + 1 part.name = tostring(current) lastPart = part parts:insert(part)
Step 20: New Apple
Lastly, we create a new apple and place it in a random position in the stage.
-- Add new apple local randomX = 25 + math.floor(math.random() * 402) local randomY = 25 + math.floor(math.random() * 258) local apple = display.newImage('apple.png', randomX, randomY) apples = display.newGroup(apple) end end
Step 21: Screen Limits
This makes the snake appear on the other side of the stage.
-- Screen Limits if(head.x < 0) then head.x = display.contentWidth - mConst elseif(head.x > display.contentWidth) then head.x = 0 end -- Vertical if(head.y < 0) then head.y = display.contentHeight - mConst elseif(head.y > display.contentHeight) then head.y = 0 end end
Step 22: Call Main Function
In order to start the game, the Main function needs to be called. With the above code in place, we’ll do that here:
Main()
Step 23: Loading Screen
The Default.png file is an image that will be displayed right when you start the application while the iOS loads the basic data to show the Main Screen. Add this image to your project source folder, it will be automatically added by the Corona compliler.
Step 24: Icon
Using the graphics you created before you can now create a nice and good looking icon. The icon size for the non-retina iPhone icon is 57x57px, but the retina version is 114x114px and the iTunes store requires a 512x512px version. I suggest creating the 512×512 version first and then scaling down for the other sizes.
It doesn’t need to have the rounded corners or the transparent glare, iTunes and the iPhone will do that for you.
Step 25: Testing in Simulator
It’s time to do the final test. Open the Corona Simulator, browse to your project folder, and then click open. If everything works as expected, you are ready for the final step!
Step 26: Build
In the Corona Simulator go to File > Build and select your target device. Fill the required data and click build. Wait a few seconds and your app will be ready for device testing and/or submission for distribution!
Conclusion
Experiment with the final result and try to make your custom version of the game!
I hope you liked this tutorial series and find it helpful. Thank you for reading!