1. Code
  2. Mobile Development
  3. Corona

Corona SDK: Create a Balloon Game - Interaction

Scroll to top
7 min read

This is the second installment in our Corona SDK Bloons inspired Game tutorial. In today's tutorial, we'll add to our interface and start coding the game interaction. 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: Declare Functions

Declare all functions as local at the start.

1
2
local Main = {}
3
local startButtonListeners = {}
4
local showCredits = {}
5
local hideCredits = {}
6
local showGameView = {}
7
local gameListeners = {}
8
local startCharge = {}
9
local charge = {}
10
local shot = {}
11
local onCollision = {}
12
local startGame = {}
13
local createBalloons = {}
14
local update = {}
15
local restartLvl = {}
16
local alert = {}
17
local restart = {}

Step 2: Constructor

Next, we'll create the function that will initialize all the game logic:

1
2
function Main()
3
	-- code...
4
end

Step 3: Add Title View

Now we place the TitleView in the stage and call a function that will add the tap listeners to the buttons.

1
2
titleBg = display.newImage('titleBg.png')
3
playBtn = display.newImage('playBtn.png', display.contentCenterX - 25.5, display.contentCenterY + 40)
4
creditsBtn = display.newImage('creditsBtn.png', display.contentCenterX - 40.5, display.contentCenterY + 85)
5
titleView = display.newGroup(titleBg, playBtn, creditsBtn)	
6
7
startButtonListeners('add')

Step 4: 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 5: 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')
6
	transition.from(creditsView, {time = 300, x = -creditsView.width, onComplete = function() creditsView:addEventListener('tap', hideCredits) creditsView.x = creditsView.x - 0.5 end})
7
end

Step 6: 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, x = -creditsView.width, onComplete = function() creditsView:removeEventListener('tap', hideCredits) display.remove(creditsView) creditsView = nil end})
6
end

Step 7: Show Game View

When the Start button is tapped the title view is tweened and removed, revealing the game view.

1
2
function showGameView:tap(e)
3
	transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil startGame() end})
4
	
5
	-- Add GFX
6
	
7
	infoBar = display.newImage('infoBar.png', 0, 276)
8
	restartBtn = display.newImage('restartBtn.png', 443, 286)
9
	squirrel = display.newImage('squirrel.png', 70, 182)
10
	gCircle = display.newImage('gCircle.png', 83, 216)
11
	gCircle:setReferencePoint(display.CenterReferencePoint)
12
	
13
	targetTF = display.newText('0', 123, 287, native.systemFontBold, 14)
14
	targetTF:setTextColor(238, 238, 238)
15
	
16
	scoreTF = display.newText('0', 196, 287, native.systemFontBold, 14)
17
	scoreTF:setTextColor(238, 238, 238)
18
	
19
	acornsTF = display.newText('5', 49, 287, native.systemFontBold, 13)
20
	acornsTF:setTextColor(238, 238, 238)
21
end

Step 8: Game Listeners

This code adds tap listeners to the game background. These will be used to shoot the acorns to the balloons. A tap listener is also added to the restart button.

1
2
function gameListeners(action)
3
	if(action == 'add') then	
4
		bg:addEventListener('touch', startCharge)
5
		bg:addEventListener('touch', shot)
6
		restartBtn:addEventListener('tap', restartLvl)
7
		Runtime:addEventListener('enterFrame', update)
8
	else	
9
		bg:removeEventListener('touch', startCharge)
10
		bg:removeEventListener('touch', shot)
11
		restartBtn:removeEventListener('tap', restartLvl)
12
		Runtime:removeEventListener('enterFrame', update)
13
	end
14
end

Step 9: Start Game

Here we start the game by hiding the direction indicator, adding the game listeners and calling the function that generates the balloons.

1
2
function startGame()
3
	-- Hide gCircle 
4
	
5
	gCircle.isVisible = false
6
	
7
	-- Create balloon function 
8
	
9
	gameListeners('add')
10
	createBalloons(5, 3)
11
end

Step 10: Create Balloons

A double for loop is used to create and place the balloons on the stage. The balloon is then added to a table. This will grant us access to the balloons outside this function.

1
2
function createBalloons(h, v)
3
	for i = 1, h do	
4
		for j = 1, v do
5
			local balloon = display.newImage('balloon.png', 300 + (i * 20), 120 + (j * 30))
6
			balloon.name = 'balloon'
7
			physics.addBody(balloon)
8
			balloon.bodyType = 'static'
9
			table.insert(balloons, balloon)
10
		end
11
	end
12
	
13
	-- Set balloon counter 
14
	
15
	targetTF.text = #balloons
16
end

Step 11: Collisions

This function handles the acorn-balloon collisions.

When this occur, the balloon is removed from the stage and a sound is played. We also update the score and target textfields.

1
2
function onCollision(e)	
3
	if(e.other.name == 'balloon') then
4
		display.remove(e.other)
5
		e.other = nil
6
		audio.play(pop)
7
		scoreTF.text = scoreTF.text + 50
8
		scoreTF:setReferencePoint(display.TopLeftReferencePoint)
9
		scoreTF.x = 196
10
		targetTF.text = targetTF.text - 1
11
	end
12
	
13
	if(targetTF.text == '0') then
14
		alert('win')
15
	end
16
end

Step 12: Start Charge

This code will reveal the direction indicator, reset the acorn's impulse variable, and add a frame listener that will handle the aim and impulse value.

1
2
function startCharge:touch(e)
3
	if(e.phase == 'began') then
4
		impulse = 0
5
		gCircle.isVisible = true
6
		Runtime:addEventListener('enterFrame', charge)
7
	end
8
end

Step 13: Charge

The aim rotates accordingly to the direction that will take the acorn, which is set by the impulse variable.

1
2
function charge()
3
	gCircle.rotation = gCircle.rotation - 3
4
	impulse = impulse - 0.2
5
	
6
	-- Prevent over rotation
7
	
8
	if(gCircle.rotation < -46) then
9
		gCircle.rotation = -46
10
		impulse = -3.2
11
	end
12
end

Step 14: Code Review

Here is the full code written in this tutorial, alongside with the comments to help you identify each part:

1
2
-- Balloons Physics Game
3
-- Developed by Carlos Yanez
4
5
-- Hide Status Bar
6
7
display.setStatusBar(display.HiddenStatusBar)
8
9
-- Physics
10
11
local physics = require('physics')
12
physics.start()
13
14
-- Graphics
15
16
-- [Background]
17
18
local bg = display.newImage('gameBg.png')
19
20
-- [Title View]
21
22
local titleBg
23
local playBtn
24
local creditsBtn
25
local titleView
26
27
-- [Credits]
28
29
local creditsView
30
31
-- [Game View]
32
33
local gCircle
34
local squirrel
35
local infoBar
36
local restartBtn
37
38
-- [TextFields]
39
40
local scoreTF
41
local targetTF
42
local acornsTF
43
44
-- Load Sound
45
46
local pop = audio.loadSound('pop.mp3')
47
48
-- Variables
49
50
local titleView
51
local credits
52
local acorns = display.newGroup()
53
local balloons = {}
54
local impulse = 0
55
local dir = 3
56
57
-- Functions
58
59
local Main = {}
60
local startButtonListeners = {}
61
local showCredits = {}
62
local hideCredits = {}
63
local showGameView = {}
64
local gameListeners = {}
65
local startCharge = {}
66
local charge = {}
67
local shot = {}
68
local onCollision = {}
69
local startGame = {}
70
local createBalloons = {}
71
local update = {}
72
local restartLvl = {}
73
local alert = {}
74
local restart = {}
75
76
-- Main Function
77
78
function Main()
79
	titleBg = display.newImage('titleBg.png')
80
	playBtn = display.newImage('playBtn.png', display.contentCenterX - 25.5, display.contentCenterY + 40)
81
	creditsBtn = display.newImage('creditsBtn.png', display.contentCenterX - 40.5, display.contentCenterY + 85)
82
	titleView = display.newGroup(titleBg, playBtn, creditsBtn)
83
	
84
	startButtonListeners('add')
85
end
86
87
function startButtonListeners(action)
88
	if(action == 'add') then
89
		playBtn:addEventListener('tap', showGameView)
90
		creditsBtn:addEventListener('tap', showCredits)
91
	else
92
		playBtn:removeEventListener('tap', showGameView)
93
		creditsBtn:removeEventListener('tap', showCredits)
94
	end
95
end
96
97
function showCredits:tap(e)
98
	playBtn.isVisible = false
99
	creditsBtn.isVisible = false
100
	creditsView = display.newImage('credits.png')
101
	transition.from(creditsView, {time = 300, x = -creditsView.width, onComplete = function() creditsView:addEventListener('tap', hideCredits) creditsView.x = creditsView.x - 0.5 end})
102
end
103
104
function hideCredits:tap(e)
105
	playBtn.isVisible = true
106
	creditsBtn.isVisible = true
107
	transition.to(creditsView, {time = 300, x = -creditsView.width, onComplete = function() creditsView:removeEventListener('tap', hideCredits) display.remove(creditsView) creditsView = nil end})
108
end
109
110
function showGameView:tap(e)
111
	transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil startGame() end})
112
	
113
	-- Add GFX
114
	
115
	infoBar = display.newImage('infoBar.png', 0, 276)
116
	restartBtn = display.newImage('restartBtn.png', 443, 286)
117
	squirrel = display.newImage('squirrel.png', 70, 182)
118
	gCircle = display.newImage('gCircle.png', 83, 216)
119
	gCircle:setReferencePoint(display.CenterReferencePoint)
120
	
121
	targetTF = display.newText('0', 123, 287, native.systemFontBold, 14)
122
	targetTF:setTextColor(238, 238, 238)
123
	
124
	scoreTF = display.newText('0', 196, 287, native.systemFontBold, 14)
125
	scoreTF:setTextColor(238, 238, 238)
126
	
127
	acornsTF = display.newText('5', 49, 287, native.systemFontBold, 13)
128
	acornsTF:setTextColor(238, 238, 238)
129
end
130
131
function gameListeners(action)
132
	if(action == 'add') then	
133
		bg:addEventListener('touch', startCharge)
134
		bg:addEventListener('touch', shot)
135
		restartBtn:addEventListener('tap', restartLvl)
136
		Runtime:addEventListener('enterFrame', update)
137
	else	
138
		bg:removeEventListener('touch', startCharge)
139
		bg:removeEventListener('touch', shot)
140
		restartBtn:removeEventListener('tap', restartLvl)
141
		Runtime:removeEventListener('enterFrame', update)
142
	end
143
end
144
145
function startGame()
146
	-- Hide gCircle 
147
	
148
	gCircle.isVisible = false
149
	
150
	-- Create balloon function 
151
	
152
	gameListeners('add')
153
	createBalloons(5, 3)
154
end
155
156
function createBalloons(h, v)
157
	for i = 1, h do	
158
		for j = 1, v do
159
			local balloon = display.newImage('balloon.png', 300 + (i * 20), 120 + (j * 30))
160
			balloon.name = 'balloon'
161
			physics.addBody(balloon)
162
			balloon.bodyType = 'static'
163
			table.insert(balloons, balloon)
164
		end
165
	end
166
	
167
	-- Set balloon counter 
168
	
169
	targetTF.text = #balloons
170
end
171
172
function onCollision(e)	
173
	--if(e.other.name == 'balloon' and e.phase == 'ended') then
174
	if(e.other.name == 'balloon') then
175
		display.remove(e.other)
176
		e.other = nil
177
		audio.play(pop)
178
		scoreTF.text = scoreTF.text + 50
179
		scoreTF:setReferencePoint(display.TopLeftReferencePoint)
180
		scoreTF.x = 196
181
		targetTF.text = targetTF.text - 1
182
	end
183
	
184
	if(targetTF.text == '0') then
185
		alert('win')
186
	end
187
end
188
189
function startCharge:touch(e)
190
	if(e.phase == 'began') then
191
		impulse = 0
192
		gCircle.isVisible = true
193
		Runtime:addEventListener('enterFrame', charge)
194
	end
195
end
196
197
function charge()
198
	gCircle.rotation = gCircle.rotation - 3
199
	impulse = impulse - 0.2
200
	
201
	-- Prevent over rotation
202
	
203
	if(gCircle.rotation < -46) then
204
		gCircle.rotation = -46
205
		impulse = -3.2
206
	end
207
end

Next Time...

In the next and final part of the series, we'll handle the acorn shooting, level restart, and the final steps to take prior to release like app testing, creating a start screen, adding an icon and, finally, building the app. Stay tuned for the final part!