1. Code
  2. Mobile Development
  3. Corona

Build a Snake Game - Adding Interaction

Scroll to top
7 min read

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.

1
2
function startButtonListeners(action)
3
	if(action == 'add') then
4
		playBtn:addEventListener('tap', showGameView)
5
		creditsBtn:addEventListener('tap', showCredits)
6
	else
7
		playBtn:removeEventListener('tap', showGameView)
8
		creditsBtn:removeEventListener('tap', showCredits)
9
	end
10
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.

1
2
function showCredits:tap(e)
3
	playBtn.isVisible = false
4
	creditsBtn.isVisible = false
5
	creditsView = display.newImage('credits.png', display.contentCenterX - 110, display.contentHeight+40)
6
	transition.to(creditsView, {time = 300, y = display.contentHeight-20, onComplete = function() creditsView:addEventListener('tap', hideCredits) end})
7
end

Step 3: Hide Credits

When the credits screen is tapped, it'll be tweened out of the stage and removed.

1
2
function hideCredits:tap(e)
3
	playBtn.isVisible = true
4
	creditsBtn.isVisible = true
5
	transition.to(creditsView, {time = 300, y = display.contentHeight+creditsView.height, onComplete = function() creditsView:removeEventListener('tap', hideCredits) display.remove(creditsView) creditsView = nil end})
6
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.

1
2
function showGameView:tap(e)
3
	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.

1
2
	-- [Add GFX]
3
	
4
	-- Game Bg
5
	
6
	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.

1
2
-- Pad
3
4
up = display.newImage('up.png', 33.5, 219.5)
5
left = display.newImage('left.png', 0, 252.5)
6
down = display.newImage('down.png', 33.5, 286.5)
7
right = display.newImage('right.png', 66.5, 252.5)
8
9
up.name = 'up'
10
down.name = 'down'
11
left.name = 'left'
12
right.name = 'right'

Step 7: Score Text

This line adds the score textfield:

1
2
-- Score Text
3
4
score = display.newText('0', 74, 3, native.systemFontBold, 15)
5
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.

1
2
-- Head
3
4
headGFX = display.newImage('head.png', display.contentCenterX-0.3, display.contentCenterY-0.2)
5
headHitArea = display.newLine(display.contentCenterX+6, display.contentCenterY-0.2, display.contentCenterX + 8, display.contentCenterY-0.2)
6
headHitArea:setColor(0, 0, 0)
7
headHitArea.width = 2
8
head = display.newGroup(headGFX, headHitArea)
9
lastPart = head
10
parts = display.newGroup()

Step 9: Initial Apple

The first apple is added by this code at a random position.

1
2
	-- Add first apple
3
	
4
	local randomX = 25 + math.floor(math.random() * 402)
5
	local randomY = 25 + math.floor(math.random() * 258)
6
		
7
	local apple = display.newImage('apple.png', randomX, randomY)
8
	apples = display.newGroup(apple)
9
	
10
	gameListeners()
11
end

Step 10: Game Listeners

This function adds the necessary listeners to start the game logic.

1
2
function gameListeners()
3
	up:addEventListener('tap', movePlayer)
4
	left:addEventListener('tap', movePlayer)
5
	down:addEventListener('tap', movePlayer)
6
	right:addEventListener('tap', movePlayer)
7
end

Step 11: Move Player Function

The direction variable is changed by this function, this will make the snake go in the pressed direction.

1
2
function movePlayer(e)
3
	dir = e.target.name
4
	
5
	if(started == nil) then
6
		timerSrc = timer.performWithDelay(speed, update, 0)
7
		started = true
8
	end
9
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.

1
2
function hitTestObjects(obj1, obj2)
3
        local left = obj1.contentBounds.xMin <= obj2.contentBounds.xMin and obj1.contentBounds.xMax >= obj2.contentBounds.xMin
4
        local right = obj1.contentBounds.xMin >= obj2.contentBounds.xMin and obj1.contentBounds.xMin <= obj2.contentBounds.xMax
5
        local up = obj1.contentBounds.yMin <= obj2.contentBounds.yMin and obj1.contentBounds.yMax >= obj2.contentBounds.yMin
6
        local down = obj1.contentBounds.yMin >= obj2.contentBounds.yMin and obj1.contentBounds.yMin <= obj2.contentBounds.yMax
7
        return (left or right) and (up or down)
8
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.

1
2
function update()
3
	
4
	-- Capture head last position, this moves the first added piece
5
	
6
	local lastX = head.x
7
	local lastY = head.y
8
	
9
	local xPos = {}
10
	local yPos = {}

Step 14: Move Parts

This complements the behavior explained in the past step.

1
2
	for i = 1, parts.numChildren do
3
		-- Capture parts position to move them
4
		
5
		xPos[i] = parts[i].x
6
		yPos[i] = parts[i].y
7
		
8
		-- Move Parts
9
		
10
		if(parts[i-1] == nil) then
11
			parts[i].x = lastX
12
			parts[i].y = lastY
13
		else
14
			parts[i].x = xPos[i-1]
15
			parts[i].y = yPos[i-1]
16
		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.

1
2
-- Game over if head touches other part of snake
3
		
4
		if(hitTestObjects(headHitArea, parts[i])) then
5
			print(parts[i].name)
6
			if(parts[i].name == '1') then
7
				print('one')
8
			else
9
				timer.cancel(timerSrc)
10
				timerSrc = nil
11
				audio.play(gameOver)
12
			end
13
		end

Step 16: Move Head

These lines move the head according to the direction stablished by the movePlayer function created in step 11.

1
2
-- Move Head & Hit Area
3
4
if(dir == 'up') then
5
    head.y = head.y - mConst
6
    
7
    headHitArea.x = headGFX.x
8
    headHitArea.y = headGFX.y - 7
9
elseif(dir == 'left') then
10
    head.x = head.x - mConst
11
    
12
    headHitArea.x = headGFX.x - 8
13
    headHitArea.y = headGFX.y
14
elseif(dir == 'down') then
15
    head.y = head.y + mConst
16
    
17
    headHitArea.x = headGFX.x
18
    headHitArea.y = headGFX.y + 8
19
elseif(dir == 'right') then
20
    head.x = head.x + mConst
21
    
22
    headHitArea.x = headGFX.x + 7
23
    headHitArea.y = headGFX.y
24
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.

1
2
-- Detect apple-player collision
3
4
for i = 1, apples.numChildren do
5
    if(hitTestObjects(head, apples[i])) then
6
        -- Remove Apple
7
        
8
        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.

1
2
-- Add Score
3
4
score.text = score.text + tostring(10)
5
score:setReferencePoint(display.TopLeftReferencePoint)
6
score.x = 74
7
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.

1
2
-- Add snake body
3
4
local part = display.newImage('bodyPart.png')
5
6
if(dir == 'up') then
7
    part.x = lastPart.x
8
    part.y = lastPart.y + mConst
9
elseif(dir == 'left') then
10
    part.x = lastPart.x + mConst
11
    part.y = lastPart.y
12
elseif(dir == 'down') then
13
    part.x = lastPart.x
14
    part.y = lastPart.y - mConst
15
elseif(dir == 'right') then
16
    part.x = lastPart.x - mConst
17
    part.y = lastPart.y
18
end
19
20
current = current + 1
21
part.name = tostring(current)
22
lastPart = part
23
parts:insert(part)

Step 20: New Apple

Lastly, we create a new apple and place it in a random position in the stage.

1
2
      -- Add new apple
3
      
4
      local randomX = 25 + math.floor(math.random() * 402)
5
      local randomY = 25 + math.floor(math.random() * 258)
6
  
7
      local apple = display.newImage('apple.png', randomX, randomY)
8
      apples = display.newGroup(apple)
9
  end
10
end

Step 21: Screen Limits

This makes the snake appear on the other side of the stage.

1
2
	-- Screen Limits
3
	
4
	if(head.x < 0) then
5
		head.x = display.contentWidth - mConst
6
	elseif(head.x > display.contentWidth) then
7
		head.x = 0
8
	end
9
	
10
	-- Vertical
11
	
12
	if(head.y < 0) then
13
		head.y = display.contentHeight - mConst
14
	elseif(head.y > display.contentHeight) then
15
		head.y = 0
16
	end
17
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:

1
2
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 512x512 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!