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.
local Main = {}
local addTitleView = {}
local startButtonListeners = {}
local showCredits = {}
local hideCredits = {}
local destroyCredits = {}
local showGameView = {}
local destroyTitleView = {}
local addListeners = {}
local newBlock = {}
local timesUp = {}
local placeBlock = {}
local blockPlaced = {}
local complete = {}
local removeBlocks = {}
local alert = {}
local alertHandler = {}
local restart = {}
Step 2: Constructor
Next we'll create the function that will initialize all the game logic:
function Main() addTitleView() end
Step 3: Add Title View
Now we place the background and titleView in the stage.
function addTitleView()
bg = display.newImage('bg.png')
title = display.newImage('title.png')
title.x = display.contentCenterX
title.y = 100
startB = display.newImage('startButton.png')
startB.x = display.contentCenterX
startB.y = display.contentCenterY
startB.name = 'startB'
creditsB = display.newImage('creditsButton.png')
creditsB.x = display.contentCenterX
creditsB.y = display.contentCenterY + 40
creditsB.name = 'creditsB'
titleView = display.newGroup()
titleView:insert(title)
titleView:insert(startB)
titleView:insert(creditsB)
startButtonListeners('add')
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.
function startButtonListeners(action)
if(action == 'add') then
creditsB:addEventListener('tap', showCredits)
startB:addEventListener('tap', showGameView)
else
creditsB:removeEventListener('tap', showCredits)
startB:removeEventListener('tap', showGameView)
end
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.
function showCredits()
credits = display.newImage('credits.png')
transition.from(credits, {time = 300, x = bg.contentWidth * 2, transition = easing.outExpo})
credits:addEventListener('tap', hideCredits)
titleView.isVisible = false
end
Step 6: Hide Credits
When the credits screen is tapped, it will be tweened out of the stage and removed.
function hideCredits()
titleView.isVisible = true
transition.to(credits, {time = 300, x = bg.contentWidth * 2, transition = easing.outExpo, onComplete = destroyCredits})
end
function destroyCredits()
credits:removeEventListener('tap', hideCredits)
display.remove(credits)
credits = nil
end
Step 7: Add Game View
When the Start button is pressed, the title view is tweened and removed, revealing the game view.
function showGameView(e)
transition.to(titleView, {time = 300, y = -titleView.height, transition = easing.inExpo, onComplete = destroyTitleView})
end
Step 8: Remove the Title View
The title view is removed from memory and the game graphics are added to the stage.
function destroyTitleView()
display.remove(titleView)
titleView = nil
-- Add GameView Graphics
up = display.newImage('container.png')
up:setReferencePoint(display.TopLeftReferencePoint)
up.x = 125
up.y = 100
right = display.newImage('container.png')
right:setReferencePoint(display.TopLeftReferencePoint)
right.x = 230
right.y = 205
down = display.newImage('container.png')
down:setReferencePoint(display.TopLeftReferencePoint)
down.x = 125
down.y = 310
left = display.newImage('container.png')
left:setReferencePoint(display.TopLeftReferencePoint)
left.x = 20
left.y = 205
holder = display.newImage('container.png')
holder:setReferencePoint(display.TopLeftReferencePoint)
holder.x = 125
holder.y = 205
-- Lives & Score Text
livesText = display.newText('Lives', 10, 10, 'Orbitron-Medium', 12)
livesText:setTextColor(163, 255, 36)
livesTF = display.newText('5', 24, 30, 'Orbitron-Medium', 12)
livesTF:setTextColor(163, 255, 36)
scoreText = display.newText('Score', 260, 10, 'Orbitron-Medium', 12)
scoreText:setTextColor(163, 255, 36)
scoreTF = display.newText('0', 274, 30, 'Orbitron-Medium', 12)
scoreTF:setTextColor(163, 255, 36)
gameView = display.newGroup()
gameView:insert(up)
gameView:insert(right)
gameView:insert(down)
gameView:insert(left)
gameView:insert(holder)
gameView:insert(livesText)
gameView:insert(livesTF)
gameView:insert(scoreText)
gameView:insert(scoreTF)
addListeners()
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.
function addListeners()
up:addEventListener('tap', placeBlock)
right:addEventListener('tap', placeBlock)
down:addEventListener('tap', placeBlock)
left:addEventListener('tap', placeBlock)
lives = 5
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
-- Create a var for every container to determine when full
up.blocks = 0
right.blocks = 0
down.blocks = 0
left.blocks = 0
-- Arrays used to remove blocks and detect color
up.blocksGFX = {}
right.blocksGFX = {}
down.blocksGFX = {}
left.blocksGFX = {}
-- Create an boolean for every container to avoid placing blocks in the same position
up.a = false
right.a = false
down.a = false
left.a = false
up.b = false
right.b = false
down.b = false
left.b = false
up.c = false
right.c = false
down.c = false
left.c = false
up.d = false
right.d = false
down.d = false
left.d = false
-- Give a name to the containers to identify them later
up.name = 'up'
right.name = 'right'
down.name = 'down'
left.name = 'left'
newBlock(true)
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.
function newBlock(firstTime)
-- New Block
local randomBlock = math.floor(math.random() * 3) + 1
local block
if(blockColor[randomBlock] == 'orange') then
block = display.newImage('orangeBlock.png')
block.name = 'orange'
block:setReferencePoint(display.TopLeftReferencePoint)
elseif(blockColor[randomBlock] == 'green') then
block = display.newImage('greenBlock.png')
block.name = 'green'
block:setReferencePoint(display.TopLeftReferencePoint)
elseif(blockColor[randomBlock] == 'purple') then
block = display.newImage('purpleBlock.png')
block.name = 'purple'
block:setReferencePoint(display.TopLeftReferencePoint)
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.
currentXPosition = positions[math.floor(math.random() * 2) + 1] currentYPosition = positions[math.floor(math.random() * 2) + 1] block.x = holder.x + currentXPosition block.y = holder.y + currentYPosition table.insert(blocks, block) 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.
local position = {currentXPosition, currentYPosition}
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
display.remove(block)
block = nil
newBlock(false)
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
display.remove(block)
block = nil
newBlock(false)
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
display.remove(block)
block = nil
newBlock(false)
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
display.remove(block)
block = nil
newBlock(false)
end
Step 14: Start Timer
The timer starts counting when the function is called for the first time.
if(firstTime) then -- Start Timer timerSource = timer.performWithDelay(3000, timesUp, 0) end 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.
function timesUp:timer(e)
-- Remove Life
lives = lives - 1
livesTF.text = lives
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.
display.remove(blocks[#blocks]) 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.
if(lives < 1) then alert() else -- Next Block newBlock(false) end end
Step 18: Code Review
Here is the full code written in this tutorial alongside with comments to help you identify each part:
-- Sort 'Frenzic' like Game
-- Developed by Carlos Yanez
-- Hide Status Bar
display.setStatusBar(display.HiddenStatusBar)
-- Graphics
-- [Background]
local bg
-- [Title View]
local title
local startB
local creditsB
-- [TitleView Group]
local titleView
-- [Score & Lives]
local livesText
local livesTF
local lives
local scoreText
local scoreTF
local score
-- [GameView]
local up
local right
local down
local left
local holder
--[GameView Group]
local gameView
-- [CreditsView]
local credits
-- Variables
local blockColor = {'orange', 'green', 'purple'}
local blocks = {}
local positions = {5, 35}
local currentXPosition
local currentYPosition
local eventTarget
local timerSource
local lives
local score
local bell
local bell4
local buzz
-- Functions
local Main = {}
local addTitleView = {}
local startButtonListeners = {}
local showCredits = {}
local hideCredits = {}
local destroyCredits = {}
local showGameView = {}
local destroyTitleView = {}
local addListeners = {}
local newBlock = {}
local timesUp = {}
local placeBlock = {}
local blockPlaced = {}
local complete = {}
local removeBlocks = {}
local alert = {}
local alertHandler = {}
local restart = {}
function Main()
addTitleView()
end
function addTitleView()
bg = display.newImage('bg.png')
title = display.newImage('title.png')
title.x = display.contentCenterX
title.y = 100
startB = display.newImage('startButton.png')
startB.x = display.contentCenterX
startB.y = display.contentCenterY
startB.name = 'startB'
creditsB = display.newImage('creditsButton.png')
creditsB.x = display.contentCenterX
creditsB.y = display.contentCenterY + 40
creditsB.name = 'creditsB'
titleView = display.newGroup()
titleView:insert(title)
titleView:insert(startB)
titleView:insert(creditsB)
startButtonListeners('add')
end
function startButtonListeners(action)
if(action == 'add') then
creditsB:addEventListener('tap', showCredits)
startB:addEventListener('tap', showGameView)
else
creditsB:removeEventListener('tap', showCredits)
startB:removeEventListener('tap', showGameView)
end
end
function showCredits()
credits = display.newImage('credits.png')
transition.from(credits, {time = 300, x = bg.contentWidth * 2, transition = easing.outExpo})
credits:addEventListener('tap', hideCredits)
titleView.isVisible = false
end
function hideCredits()
titleView.isVisible = true
transition.to(credits, {time = 300, x = bg.contentWidth * 2, transition = easing.outExpo, onComplete = destroyCredits})
end
function destroyCredits()
credits:removeEventListener('tap', hideCredits)
display.remove(credits)
credits = nil
end
function showGameView(e)
transition.to(titleView, {time = 300, y = -titleView.height, transition = easing.inExpo, onComplete = destroyTitleView})
end
function destroyTitleView()
display.remove(titleView)
titleView = nil
-- Add GameView Graphics
up = display.newImage('container.png')
up:setReferencePoint(display.TopLeftReferencePoint)
up.x = 125
up.y = 100
right = display.newImage('container.png')
right:setReferencePoint(display.TopLeftReferencePoint)
right.x = 230
right.y = 205
down = display.newImage('container.png')
down:setReferencePoint(display.TopLeftReferencePoint)
down.x = 125
down.y = 310
left = display.newImage('container.png')
left:setReferencePoint(display.TopLeftReferencePoint)
left.x = 20
left.y = 205
holder = display.newImage('container.png')
holder:setReferencePoint(display.TopLeftReferencePoint)
holder.x = 125
holder.y = 205
-- Lives & Score Text
livesText = display.newText('Lives', 10, 10, 'Orbitron-Medium', 12)
livesText:setTextColor(163, 255, 36)
livesTF = display.newText('5', 24, 30, 'Orbitron-Medium', 12)
livesTF:setTextColor(163, 255, 36)
scoreText = display.newText('Score', 260, 10, 'Orbitron-Medium', 12)
scoreText:setTextColor(163, 255, 36)
scoreTF = display.newText('0', 274, 30, 'Orbitron-Medium', 12)
scoreTF:setTextColor(163, 255, 36)
gameView = display.newGroup()
gameView:insert(up)
gameView:insert(right)
gameView:insert(down)
gameView:insert(left)
gameView:insert(holder)
gameView:insert(livesText)
gameView:insert(livesTF)
gameView:insert(scoreText)
gameView:insert(scoreTF)
addListeners()
end
function addListeners()
up:addEventListener('tap', placeBlock)
right:addEventListener('tap', placeBlock)
down:addEventListener('tap', placeBlock)
left:addEventListener('tap', placeBlock)
lives = 5
score = 0
-- Create a var for every container to determine when full
up.blocks = 0
right.blocks = 0
down.blocks = 0
left.blocks = 0
-- Arrays used to remove blocks and detect color
up.blocksGFX = {}
right.blocksGFX = {}
down.blocksGFX = {}
left.blocksGFX = {}
-- Create an boolean for every container to avoid placing blocks in the same position
up.a = false
right.a = false
down.a = false
left.a = false
up.b = false
right.b = false
down.b = false
left.b = false
up.c = false
right.c = false
down.c = false
left.c = false
up.d = false
right.d = false
down.d = false
left.d = false
-- Give a name to the containers to identify them later
up.name = 'up'
right.name = 'right'
down.name = 'down'
left.name = 'left'
newBlock(true)
end
function newBlock(firstTime)
-- New Block
local randomBlock = math.floor(math.random() * 3) + 1
local block
if(blockColor[randomBlock] == 'orange') then
block = display.newImage('orangeBlock.png')
block.name = 'orange'
block:setReferencePoint(display.TopLeftReferencePoint)
elseif(blockColor[randomBlock] == 'green') then
block = display.newImage('greenBlock.png')
block.name = 'green'
block:setReferencePoint(display.TopLeftReferencePoint)
elseif(blockColor[randomBlock] == 'purple') then
block = display.newImage('purpleBlock.png')
block.name = 'purple'
block:setReferencePoint(display.TopLeftReferencePoint)
end
currentXPosition = positions[math.floor(math.random() * 2) + 1]
currentYPosition = positions[math.floor(math.random() * 2) + 1]
block.x = holder.x + currentXPosition
block.y = holder.y + currentYPosition
table.insert(blocks, block)
gameView:insert(block)
-- Check for an available space to move the block
local position = {currentXPosition, currentYPosition}
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
display.remove(block)
block = nil
newBlock(false)
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
display.remove(block)
block = nil
newBlock(false)
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
display.remove(block)
block = nil
newBlock(false)
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
display.remove(block)
block = nil
newBlock(false)
end
-- Start Timer the first time the function is called
if(firstTime) then
-- Start Timer
timerSource = timer.performWithDelay(3000, timesUp, 0)
end
end
function timesUp:timer(e)
-- Remove Live
lives = lives - 1
livesTF.text = lives
media.playEventSound('buzz.caf')
-- Remove Unused Block
display.remove(blocks[#blocks])
table.remove(blocks)
-- Check if out of lives
if(lives < 1) then
--alert()
else
-- Next Block
newBlock(false)
end
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!
Comments