Advertisement
  1. Code
  2. Mobile Development
  3. Corona

Corona SDK: Develop a Frenzic-like Game - Adding Interactivity

Scroll to top
Read Time: 14 min
This post is part of a series called Corona SDK: Develop a Frenzic-like Game.
Corona SDK: Develop a Frenzic-like Game - Application Setup
Corona SDK: Develop a Frenzic-like Game - Final Steps

Welcome to the second post in our Corona SDK Frenzic-like game series. In today's tutorial, we'll be adding interactivity to our interface and coding the start of the game.

Where We Left Off. . .

Please be sure to check part 1 of the series to fully understand this tutorial.

Step 1: Declare Functions

Declare all functions as local at the start.

1
2
local Main = {}
3
local addTitleView = {}
4
local startButtonListeners = {}
5
local showCredits = {}
6
local hideCredits = {}
7
local destroyCredits = {}
8
local showGameView = {}
9
local destroyTitleView = {}
10
local addListeners = {}
11
local newBlock = {}
12
local timesUp = {}
13
local placeBlock = {}
14
local blockPlaced = {}
15
local complete = {}
16
local removeBlocks = {}
17
local alert = {}
18
local alertHandler = {}
19
local restart = {}

Step 2: Constructor

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

1
2
function Main()
3
	addTitleView()
4
end

Step 3: Add Title View

Now we place the background and titleView in the stage.

1
2
function addTitleView()
3
	bg = display.newImage('bg.png')
4
	
5
	title = display.newImage('title.png')
6
	title.x = display.contentCenterX
7
	title.y = 100
8
	
9
	startB = display.newImage('startButton.png')
10
	startB.x = display.contentCenterX
11
	startB.y = display.contentCenterY
12
	startB.name = 'startB'
13
	
14
	creditsB = display.newImage('creditsButton.png')
15
	creditsB.x = display.contentCenterX
16
	creditsB.y = display.contentCenterY + 40
17
	creditsB.name = 'creditsB'
18
	
19
	titleView = display.newGroup()
20
	titleView:insert(title)
21
	titleView:insert(startB)
22
	titleView:insert(creditsB)
23
	
24
	startButtonListeners('add')
25
end

Step 4: Button Listeners

In this function we add the tap listeners to the buttons in the title view, this will take us to the game screen or the credits screen.

1
2
function startButtonListeners(action)
3
	if(action == 'add') then
4
		creditsB:addEventListener('tap', showCredits)
5
		startB:addEventListener('tap', showGameView)
6
	else
7
		creditsB:removeEventListener('tap', showCredits)
8
		startB:removeEventListener('tap', showGameView)
9
	end
10
end

Step 5: Show Credits

The credits screen is shown when the user taps the credits button, a tap listener is added to the credits view to remove it.

1
2
function showCredits()
3
	credits = display.newImage('credits.png')
4
	transition.from(credits, {time = 300, x = bg.contentWidth * 2, transition = easing.outExpo})
5
	credits:addEventListener('tap', hideCredits)
6
	titleView.isVisible = false
7
end

Step 6: Hide Credits

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

1
2
function hideCredits()
3
	titleView.isVisible = true
4
	transition.to(credits, {time = 300, x = bg.contentWidth * 2, transition = easing.outExpo, onComplete = destroyCredits})
5
end
6
7
function destroyCredits()
8
	credits:removeEventListener('tap', hideCredits)
9
	display.remove(credits)
10
	credits = nil
11
end

Step 7: Add Game View

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

1
2
function showGameView(e)
3
	transition.to(titleView, {time = 300, y = -titleView.height, transition = easing.inExpo, onComplete = destroyTitleView})
4
end

Step 8: Remove the Title View

The title view is removed from memory and the game graphics are added to the stage.

1
2
function destroyTitleView()
3
	display.remove(titleView)
4
	titleView = nil
5
	
6
	-- Add GameView Graphics
7
	
8
	up = display.newImage('container.png')
9
	up:setReferencePoint(display.TopLeftReferencePoint)
10
	up.x = 125
11
	up.y = 100
12
	
13
	right = display.newImage('container.png')
14
	right:setReferencePoint(display.TopLeftReferencePoint)
15
	right.x = 230
16
	right.y = 205
17
	
18
	down = display.newImage('container.png')
19
	down:setReferencePoint(display.TopLeftReferencePoint)
20
	down.x = 125
21
	down.y = 310
22
	
23
	left = display.newImage('container.png')
24
	left:setReferencePoint(display.TopLeftReferencePoint)
25
	left.x = 20
26
	left.y = 205
27
	
28
	holder = display.newImage('container.png')
29
	holder:setReferencePoint(display.TopLeftReferencePoint)
30
	holder.x = 125
31
	holder.y = 205
32
	
33
	-- Lives & Score Text
34
	
35
	livesText = display.newText('Lives', 10, 10, 'Orbitron-Medium', 12)
36
	livesText:setTextColor(163, 255, 36)
37
	
38
	livesTF = display.newText('5', 24, 30, 'Orbitron-Medium', 12)
39
	livesTF:setTextColor(163, 255, 36)
40
	
41
	scoreText = display.newText('Score', 260, 10, 'Orbitron-Medium', 12)
42
	scoreText:setTextColor(163, 255, 36)
43
	
44
	scoreTF = display.newText('0', 274, 30, 'Orbitron-Medium', 12)
45
	scoreTF:setTextColor(163, 255, 36)
46
	
47
	gameView = display.newGroup()
48
	gameView:insert(up)
49
	gameView:insert(right)
50
	gameView:insert(down)
51
	gameView:insert(left)
52
	gameView:insert(holder)
53
	gameView:insert(livesText)
54
	gameView:insert(livesTF)
55
	gameView:insert(scoreText)
56
	gameView:insert(scoreTF)
57
	
58
	addListeners()
59
end

Step 9: Game Listeners

This function will add a Tap Listener to the square containers so you can tap them and place the current block in the center (holder) container.

1
2
function addListeners()
3
	up:addEventListener('tap', placeBlock)
4
	right:addEventListener('tap', placeBlock)
5
	down:addEventListener('tap', placeBlock)
6
	left:addEventListener('tap', placeBlock)
7
	
8
	lives = 5
9
	score = 0

Step 10: Container Variables

These variables are created inside the square containers, they are used to register the blocks, colors, and positions inside every square.

The letters represent the following positions:

  • a: top-left
  • b: top-right
  • c: bottom-left
  • d: bottom-right
1
2
-- Create a var for every container to determine when full
3
	
4
	up.blocks = 0
5
	right.blocks = 0
6
	down.blocks = 0
7
	left.blocks = 0
8
	
9
	-- Arrays used to remove blocks and detect color
10
	
11
	up.blocksGFX = {}
12
	right.blocksGFX = {}
13
	down.blocksGFX = {}
14
	left.blocksGFX = {}
15
	
16
	-- Create an boolean for every container to avoid placing blocks in the same position 
17
	
18
	up.a = false
19
	right.a = false
20
	down.a = false
21
	left.a = false 
22
	
23
	up.b = false
24
	right.b = false
25
	down.b = false
26
	left.b = false 
27
	
28
	up.c = false
29
	right.c = false
30
	down.c = false
31
	left.c = false 
32
	
33
	up.d = false
34
	right.d = false
35
	down.d = false
36
	left.d = false 
37
	
38
	-- Give a name to the containers to identify them later
39
	
40
	up.name = 'up'
41
	right.name = 'right'
42
	down.name = 'down'
43
	left.name = 'left'
44
	
45
	newBlock(true)
46
end

Step 11: Generate Random Block

This code picks a random block color from the Table, this will be used to instantiate the actual block. A parameter is used to determine if the timer needs to be started.

1
2
function newBlock(firstTime)
3
	-- New Block 
4
	
5
	local randomBlock = math.floor(math.random() * 3) + 1
6
	local block
7
	
8
	if(blockColor[randomBlock] == 'orange') then
9
		block = display.newImage('orangeBlock.png')
10
		block.name = 'orange'
11
		block:setReferencePoint(display.TopLeftReferencePoint)
12
	elseif(blockColor[randomBlock] == 'green') then
13
		block = display.newImage('greenBlock.png')
14
		block.name = 'green'
15
		block:setReferencePoint(display.TopLeftReferencePoint)
16
	elseif(blockColor[randomBlock] == 'purple') then
17
		block = display.newImage('purpleBlock.png')
18
		block.name = 'purple'
19
		block:setReferencePoint(display.TopLeftReferencePoint)
20
	end

Step 12: Add a New Block

After selecting the block color, the position where it will be placed its calculated using the positions table and then added to the blocks table and the stage.

1
2
currentXPosition = positions[math.floor(math.random() * 2) + 1]
3
	currentYPosition = positions[math.floor(math.random() * 2) + 1]
4
	
5
	block.x = holder.x + currentXPosition
6
	block.y = holder.y + currentYPosition
7
	table.insert(blocks, block)
8
	gameView:insert(block)

Step 13: Check Available Space

Before continuing with the game, we must check that the newly created block can actually be placed in a square container. The following code checks every container array to make sure that there is a position available to place the block. If not, the block is destroyed and the function is called again to generate another one.

1
2
local position = {currentXPosition, currentYPosition}
3
	
4
	if(position[1] == 5 and position[2] == 5 and up.a == true and right.a == true  and down.a == true  and left.a == true ) then
5
		display.remove(block)
6
		block = nil
7
		newBlock(false)
8
	elseif(position[1] == 35 and position[2] == 5 and up.b == true  and right.b == true  and down.b == true  and left.b == true ) then
9
		display.remove(block)
10
		block = nil
11
		newBlock(false)
12
	elseif(position[1] == 5 and position[2] == 35 and up.c == true  and right.c == true  and down.c == true  and left.c == true ) then
13
		display.remove(block)
14
		block = nil
15
		newBlock(false)
16
	elseif(position[1] == 35 and position[2] == 35 and up.d == true  and right.d == true  and down.d == true  and left.d == true ) then
17
		display.remove(block)
18
		block = nil
19
		newBlock(false)
20
	end

Step 14: Start Timer

The timer starts counting when the function is called for the first time.

1
2
if(firstTime) then
3
		-- Start Timer 	
4
		timerSource = timer.performWithDelay(3000, timesUp, 0)
5
	end
6
end

Step 15: Lives

Three seconds are given to place a block in a square container, if that time passes and the block is still in the center square, a life will be removed.

1
2
function timesUp:timer(e)
3
	-- Remove Life 
4
	
5
	lives = lives - 1
6
	livesTF.text = lives
7
	media.playEventSound('buzz.caf')

Step 16: Unused Blocks

After removing the life, the block in the center square will be destroyed and a new block will be generated.

1
2
display.remove(blocks[#blocks])
3
table.remove(blocks)

Step 17: Check for Game Over

This code checks if the player is out of lives and calls a function that will handle that.

1
2
	if(lives < 1) then
3
		alert()
4
	else
5
		-- Next Block 
6
		
7
		newBlock(false)
8
	end
9
end

Step 18: Code Review

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

1
2
-- Sort 'Frenzic' like Game
3
-- Developed by Carlos Yanez
4
5
-- Hide Status Bar
6
7
display.setStatusBar(display.HiddenStatusBar)
8
9
-- Graphics
10
-- [Background]
11
12
local bg
13
14
-- [Title View]
15
 
16
local title
17
local startB
18
local creditsB
19
20
-- [TitleView Group]
21
22
local titleView
23
24
-- [Score & Lives]
25
26
local livesText
27
local livesTF
28
local lives
29
local scoreText
30
local scoreTF
31
local score
32
33
-- [GameView]
34
35
local up
36
local right
37
local down
38
local left
39
local holder
40
41
--[GameView Group]
42
43
local gameView
44
45
-- [CreditsView]
46
47
local credits
48
49
-- Variables
50
51
local blockColor = {'orange', 'green', 'purple'}
52
local blocks = {}
53
local positions = {5, 35}
54
local currentXPosition
55
local currentYPosition
56
local eventTarget
57
local timerSource
58
local lives
59
local score
60
local bell
61
local bell4
62
local buzz
63
64
-- Functions
65
66
local Main = {}
67
local addTitleView = {}
68
local startButtonListeners = {}
69
local showCredits = {}
70
local hideCredits = {}
71
local destroyCredits = {}
72
local showGameView = {}
73
local destroyTitleView = {}
74
local addListeners = {}
75
local newBlock = {}
76
local timesUp = {}
77
local placeBlock = {}
78
local blockPlaced = {}
79
local complete = {}
80
local removeBlocks = {}
81
local alert = {}
82
local alertHandler = {}
83
local restart = {}
84
85
function Main()
86
	addTitleView()
87
end
88
89
function addTitleView()
90
	bg = display.newImage('bg.png')
91
	
92
	title = display.newImage('title.png')
93
	title.x = display.contentCenterX
94
	title.y = 100
95
	
96
	startB = display.newImage('startButton.png')
97
	startB.x = display.contentCenterX
98
	startB.y = display.contentCenterY
99
	startB.name = 'startB'
100
	
101
	creditsB = display.newImage('creditsButton.png')
102
	creditsB.x = display.contentCenterX
103
	creditsB.y = display.contentCenterY + 40
104
	creditsB.name = 'creditsB'
105
	
106
	titleView = display.newGroup()
107
	titleView:insert(title)
108
	titleView:insert(startB)
109
	titleView:insert(creditsB)
110
	
111
	startButtonListeners('add')
112
end
113
114
function startButtonListeners(action)
115
	if(action == 'add') then
116
		creditsB:addEventListener('tap', showCredits)
117
		startB:addEventListener('tap', showGameView)
118
	else
119
		creditsB:removeEventListener('tap', showCredits)
120
		startB:removeEventListener('tap', showGameView)
121
	end
122
end
123
124
function showCredits()
125
	credits = display.newImage('credits.png')
126
	transition.from(credits, {time = 300, x = bg.contentWidth * 2, transition = easing.outExpo})
127
	credits:addEventListener('tap', hideCredits)
128
	titleView.isVisible = false
129
end
130
131
function hideCredits()
132
	titleView.isVisible = true
133
	transition.to(credits, {time = 300, x = bg.contentWidth * 2, transition = easing.outExpo, onComplete = destroyCredits})
134
end
135
136
function destroyCredits()
137
	credits:removeEventListener('tap', hideCredits)
138
	display.remove(credits)
139
	credits = nil
140
end
141
142
function showGameView(e)
143
	transition.to(titleView, {time = 300, y = -titleView.height, transition = easing.inExpo, onComplete = destroyTitleView})
144
end
145
146
function destroyTitleView()
147
	display.remove(titleView)
148
	titleView = nil
149
	
150
	-- Add GameView Graphics
151
	
152
	up = display.newImage('container.png')
153
	up:setReferencePoint(display.TopLeftReferencePoint)
154
	up.x = 125
155
	up.y = 100
156
	
157
	right = display.newImage('container.png')
158
	right:setReferencePoint(display.TopLeftReferencePoint)
159
	right.x = 230
160
	right.y = 205
161
	
162
	down = display.newImage('container.png')
163
	down:setReferencePoint(display.TopLeftReferencePoint)
164
	down.x = 125
165
	down.y = 310
166
	
167
	left = display.newImage('container.png')
168
	left:setReferencePoint(display.TopLeftReferencePoint)
169
	left.x = 20
170
	left.y = 205
171
	
172
	holder = display.newImage('container.png')
173
	holder:setReferencePoint(display.TopLeftReferencePoint)
174
	holder.x = 125
175
	holder.y = 205
176
	
177
	-- Lives & Score Text
178
	
179
	livesText = display.newText('Lives', 10, 10, 'Orbitron-Medium', 12)
180
	livesText:setTextColor(163, 255, 36)
181
	
182
	livesTF = display.newText('5', 24, 30, 'Orbitron-Medium', 12)
183
	livesTF:setTextColor(163, 255, 36)
184
	
185
	scoreText = display.newText('Score', 260, 10, 'Orbitron-Medium', 12)
186
	scoreText:setTextColor(163, 255, 36)
187
	
188
	scoreTF = display.newText('0', 274, 30, 'Orbitron-Medium', 12)
189
	scoreTF:setTextColor(163, 255, 36)
190
	
191
	gameView = display.newGroup()
192
	gameView:insert(up)
193
	gameView:insert(right)
194
	gameView:insert(down)
195
	gameView:insert(left)
196
	gameView:insert(holder)
197
	gameView:insert(livesText)
198
	gameView:insert(livesTF)
199
	gameView:insert(scoreText)
200
	gameView:insert(scoreTF)
201
	
202
	addListeners()
203
end
204
205
function addListeners()
206
	up:addEventListener('tap', placeBlock)
207
	right:addEventListener('tap', placeBlock)
208
	down:addEventListener('tap', placeBlock)
209
	left:addEventListener('tap', placeBlock)
210
	
211
	lives = 5
212
	score = 0
213
	
214
	-- Create a var for every container to determine when full
215
	
216
	up.blocks = 0
217
	right.blocks = 0
218
	down.blocks = 0
219
	left.blocks = 0
220
	
221
	-- Arrays used to remove blocks and detect color
222
	
223
	up.blocksGFX = {}
224
	right.blocksGFX = {}
225
	down.blocksGFX = {}
226
	left.blocksGFX = {}
227
	
228
	-- Create an boolean for every container to avoid placing blocks in the same position 
229
	
230
	up.a = false
231
	right.a = false
232
	down.a = false
233
	left.a = false 
234
	
235
	up.b = false
236
	right.b = false
237
	down.b = false
238
	left.b = false 
239
	
240
	up.c = false
241
	right.c = false
242
	down.c = false
243
	left.c = false 
244
	
245
	up.d = false
246
	right.d = false
247
	down.d = false
248
	left.d = false 
249
	
250
	-- Give a name to the containers to identify them later
251
	
252
	up.name = 'up'
253
	right.name = 'right'
254
	down.name = 'down'
255
	left.name = 'left'
256
	
257
	newBlock(true)
258
end
259
260
function newBlock(firstTime)
261
	-- New Block 
262
	
263
	local randomBlock = math.floor(math.random() * 3) + 1
264
	local block
265
	
266
	if(blockColor[randomBlock] == 'orange') then
267
		block = display.newImage('orangeBlock.png')
268
		block.name = 'orange'
269
		block:setReferencePoint(display.TopLeftReferencePoint)
270
	elseif(blockColor[randomBlock] == 'green') then
271
		block = display.newImage('greenBlock.png')
272
		block.name = 'green'
273
		block:setReferencePoint(display.TopLeftReferencePoint)
274
	elseif(blockColor[randomBlock] == 'purple') then
275
		block = display.newImage('purpleBlock.png')
276
		block.name = 'purple'
277
		block:setReferencePoint(display.TopLeftReferencePoint)
278
	end
279
	
280
	currentXPosition = positions[math.floor(math.random() * 2) + 1]
281
	currentYPosition = positions[math.floor(math.random() * 2) + 1]
282
	
283
	block.x = holder.x + currentXPosition
284
	block.y = holder.y + currentYPosition
285
	table.insert(blocks, block)
286
	gameView:insert(block)
287
	
288
	-- Check for an available space to move the block 
289
	
290
	local position = {currentXPosition, currentYPosition}
291
	
292
	if(position[1] == 5 and position[2] == 5 and up.a == true and right.a == true  and down.a == true  and left.a == true ) then
293
		display.remove(block)
294
		block = nil
295
		newBlock(false)
296
	elseif(position[1] == 35 and position[2] == 5 and up.b == true  and right.b == true  and down.b == true  and left.b == true ) then
297
		display.remove(block)
298
		block = nil
299
		newBlock(false)
300
	elseif(position[1] == 5 and position[2] == 35 and up.c == true  and right.c == true  and down.c == true  and left.c == true ) then
301
		display.remove(block)
302
		block = nil
303
		newBlock(false)
304
	elseif(position[1] == 35 and position[2] == 35 and up.d == true  and right.d == true  and down.d == true  and left.d == true ) then
305
		display.remove(block)
306
		block = nil
307
		newBlock(false)
308
	end
309
	
310
	-- Start Timer the first time the function is called 
311
	
312
	if(firstTime) then
313
		-- Start Timer 	
314
		timerSource = timer.performWithDelay(3000, timesUp, 0)
315
	end
316
end
317
318
function timesUp:timer(e)
319
	-- Remove Live 
320
	
321
	lives = lives - 1
322
	livesTF.text = lives
323
	media.playEventSound('buzz.caf')
324
	
325
	-- Remove Unused Block 
326
	
327
	display.remove(blocks[#blocks])
328
	table.remove(blocks)
329
	
330
	-- Check if out of lives 
331
	
332
	if(lives < 1) then
333
		--alert()
334
	else
335
		-- Next Block 
336
		
337
		newBlock(false)
338
	end
339
end

Next Time...

In the next and final part of the series, we'll handle the blocks behavior, scores, and the final steps to take prior to releasing the app -like app testing, creating a start screen, adding an icon, and building the app. Stay tuned for the final part!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.