Corona SDK: Create a Rapid Roll-like Game – Interaction

Corona SDK: Create a Rapid Roll-like Game – Interaction

Tutorial Details
  • Technology: Corona SDK
  • Difficulty: Intermediate
  • Completion Time: 30 - 60 Minutes

This is the second installment in our Corona SDK Rapid Roll 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.

local Main = {}
local startButtonListeners = {}
local showCredits = {}
local hideCredits = {}
local showGameView = {}
local placeBet = {}
local randomShellMove = {}
local checkMovesLeft = {}
local revealBall = {}
local alert = {}

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 TitleView in the stage and call a function that will add the tap listeners to the buttons.

function addTitleView()
	bg = display.newImage('bg.png')
	title = display.newImage('titleBg.png')
	startB = display.newImage('startBtn.png')
	startB.x = display.contentCenterX
	startB.y = display.contentCenterY
	startB.name = 'startB'
	creditsB = display.newImage('creditsBtn.png')
	creditsB.x = display.contentCenterX
	creditsB.y = display.contentCenterY + 60
	creditsB.name = 'creditsB'
	titleView = display.newGroup()
	titleView:insert(title)
	titleView:insert(startB)
	titleView:insert(creditsB)
	initialListeners('add')
end

Step 4: Start Button Listeners

This function adds the necesary listeners to the TitleView buttons.

function initialListeners(action)
	if(action == 'add') then
		startB:addEventListener('tap', gameView)
		creditsB:addEventListener('tap', showCredits)
	else
		startB:removeEventListener('tap', gameView)
		creditsB:removeEventListener('tap', showCredits)
	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('creditsView.png')
	transition.from(credits, {time = 400, x = display.contentWidth * 2, transition = easing.outExpo})
	credits:addEventListener('tap', hideCredits)
	startB.isVisible = false
	creditsB.isVisible = false
end

Step 6: Hide Credits

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

function hideCredits()
	startB.isVisible = true
	creditsB.isVisible = true
	transition.to(credits, {time = 600, x = display.contentWidth * 2, transition = easing.outExpo, onComplete = destroyCredits})
end
function destroyCredits()
	credits:removeEventListener('tap', hideCredits)
	display.remove(credits)
	credits = nil
end

Step 7: Remove Title View

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

function gameView()
	initialListeners('rmv')
	-- Remove MenuView, Start Game
	transition.to(titleView, {time = 500, y = -titleView.height, onComplete = function()display.remove(titleView) titleView = nil addInitialBlocks(3)end})

Step 8: Score & Lives Text

This code creates the Score and Lives text and places them in the stage.

	-- Score Text
	scoreTF = display.newText('0', 303, 22, system.nativeFont, 12)
	scoreTF:setTextColor(68, 68, 68)
	-- Lives Text
	livesTF = display.newText('x3', 289, 56, system.nativeFont, 12)
	livesTF:setTextColor(245, 249, 248)
end

Step 9: Add Initial Blocks

The following function adds the blocks specified in the parameter at a random position, it will also call the function to add the player to the stage.

function addInitialBlocks(n)
	blocks = display.newGroup()
	for i = 1, n do
		local block = display.newImage('block.png')
		block.x = math.floor(math.random() * (display.contentWidth - block.width))
		block.y = (display.contentHeight * 0.5) + math.floor(math.random() * (display.contentHeight * 0.5))
		physics.addBody(block, {density = 1, bounce = 0})
		block.bodyType = 'static'
		blocks:insert(block)
	end
	addPlayer()
end

Step 10: Add Player

The player will be added when the initial blocks are in stage. It will appear in the center X of the stage.

function addPlayer()
	player = display.newImage('player.png')
	player.x = (display.contentWidth * 0.5)
	player.y = player.height
	physics.addBody(player, {density = 1, friction = 0, bounce = 0})
	player.isFixedRotation = true
	gameListeners('add')
end

Step 11: Move Player

The Accelerometer is used to move the player across the screen, the value is calculated using the xGravity property.

function movePlayer:accelerometer(e)
	-- Accelerometer Movement
	player.x = display.contentCenterX + (display.contentCenterX * (e.xGravity*3))

Step 12: Screen Borders

This code prevents the player from going offscreen on the sides.

	-- Borders
	if((player.x - player.width * 0.5) < 0) then
		player.x = player.width * 0.5
	elseif((player.x + player.width * 0.5) > display.contentWidth) then
		player.x = display.contentWidth - player.width * 0.5
	end
end

Step 13: Add Regular Block

This function is called by a Timer. It will calculate a random number between 1 and 4 and when the result equals 1, a bad block will be added. If the result is different than 1, a normal block will be instantiated. The blocks are added to a Table, this way we are able to access them outside this function.

function addBlock()
	local r = math.floor(math.random() * 4)
	if(r ~= 0) then
		local block = display.newImage('block.png')
		block.x = math.random() * (display.contentWidth - (block.width * 0.5))
		block.y = display.contentHeight + block.height
		physics.addBody(block, {density = 1, bounce = 0})
		block.bodyType = 'static'
		blocks:insert(block)
    else
		local badBlock = display.newImage('badBlock.png')
		badBlock.name = 'bad'
		physics.addBody(badBlock, {density = 1, bounce = 0})
		badBlock.bodyType = 'static'
		badBlock.x = math.random() * (display.contentWidth - (badBlock.width * 0.5))
		badBlock.y = display.contentHeight + badBlock.height
		blocks:insert(badBlock)
	end
end

Step 14: Add Live Graphic

Another timed function, a live graphic will be added when the timer is complete. The live position will be the last block in the table – 1.

function addLive()
	live = display.newImage('live.png')
	live.name = 'live'
	live.x = blocks[blocks.numChildren - 1].x
	live.y = blocks[blocks.numChildren - 1].y - live.height
	physics.addBody(live, {density = 1, friction = 0, bounce = 0})
end

Step 15: Game Listeners

This function adds and removes the necessary listeners to start the game.

function gameListeners(action)
	if(action == 'add') then
		Runtime:addEventListener('accelerometer', movePlayer)
		Runtime:addEventListener('enterFrame', update)
		blockTimer = timer.performWithDelay(800, addBlock, 0)
		liveTimer = timer.performWithDelay(8000, addLive, 0)
		player:addEventListener('collision', collisionHandler)
	else
		Runtime:removeEventListener('accelerometer', movePlayer)
		Runtime:removeEventListener('enterFrame', update)
		timer.cancel(blockTimer)
		timer.cancel(liveTimer)
		blockTimer = nil
		liveTimer = nil
		player:removeEventListener('collision', collisionHandler)
	end
end

Step 16: Code Review

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

-- Blocks 'Rapid Roll' like Game
-- Developed by Carlos Yanez
-- Hide Status Bar
display.setStatusBar(display.HiddenStatusBar)
-- Physics
local physics = require('physics')
physics.start()
physics.setGravity(0, 0)
-- Graphics
-- [Background]
local bg
-- [Title View]
local title
local startB
local creditsB
-- [TitleView Group]
local titleView
-- [CreditsView]
local credits
-- [Score & Lives]
local live
local livesTF
local lives = 3
local scoreTF
local score = 0
local alertScore
-- [Blocks group, Player]
local blocks
local player
--[GameView Group]
local gameView
-- Variables
local moveSpeed = 2
local blockTimer
local liveTimer
-- Functions
local Main = {}
local addTitleView = {}
local initialListeners = {}
local showCredits = {}
local hideCredits = {}
local destroyCredits = {}
local gameView = {}
local addInitialBlocks = {}
local addPlayer = {}
local movePlayer = {}
local addBlock = {}
local addLive = {}
local gameListeners = {}
local update = {}
local collisionHandler = {}
local showAlert = {}
function Main()
	addTitleView()
end
function addTitleView()
	bg = display.newImage('bg.png')
	title = display.newImage('titleBg.png')
	startB = display.newImage('startBtn.png')
	startB.x = display.contentCenterX
	startB.y = display.contentCenterY
	startB.name = 'startB'
	creditsB = display.newImage('creditsBtn.png')
	creditsB.x = display.contentCenterX
	creditsB.y = display.contentCenterY + 60
	creditsB.name = 'creditsB'
	titleView = display.newGroup()
	titleView:insert(title)
	titleView:insert(startB)
	titleView:insert(creditsB)
	initialListeners('add')
end
function initialListeners(action)
	if(action == 'add') then
		startB:addEventListener('tap', gameView)
		creditsB:addEventListener('tap', showCredits)
	else
		startB:removeEventListener('tap', gameView)
		creditsB:removeEventListener('tap', showCredits)
	end
end
function showCredits()
	credits = display.newImage('creditsView.png')
	transition.from(credits, {time = 400, x = display.contentWidth * 2, transition = easing.outExpo})
	credits:addEventListener('tap', hideCredits)
	startB.isVisible = false
	creditsB.isVisible = false
end
function hideCredits()
	startB.isVisible = true
	creditsB.isVisible = true
	transition.to(credits, {time = 600, x = display.contentWidth * 2, transition = easing.outExpo, onComplete = destroyCredits})
end
function destroyCredits()
	credits:removeEventListener('tap', hideCredits)
	display.remove(credits)
	credits = nil
end
function gameView()
	initialListeners('rmv')
	-- Remove MenuView, Start Game
	transition.to(titleView, {time = 500, y = -titleView.height, onComplete = function()display.remove(titleView) titleView = nil addInitialBlocks(3)end})
	-- Score Text
	scoreTF = display.newText('0', 303, 22, system.nativeFont, 12)
	scoreTF:setTextColor(68, 68, 68)
	-- Lives Text
	livesTF = display.newText('x3', 289, 56, system.nativeFont, 12)
	livesTF:setTextColor(245, 249, 248)
end
function addInitialBlocks(n)
	blocks = display.newGroup()
	for i = 1, n do
		local block = display.newImage('block.png')
		block.x = math.floor(math.random() * (display.contentWidth - block.width))
		block.y = (display.contentHeight * 0.5) + math.floor(math.random() * (display.contentHeight * 0.5))
		physics.addBody(block, {density = 1, bounce = 0})
		block.bodyType = 'static'
		blocks:insert(block)
	end
	addPlayer()
end
function addPlayer()
	player = display.newImage('player.png')
	player.x = (display.contentWidth * 0.5)
	player.y = player.height
	physics.addBody(player, {density = 1, friction = 0, bounce = 0})
	player.isFixedRotation = true
	gameListeners('add')
end
function movePlayer:accelerometer(e)
	-- Accelerometer Movement
	player.x = display.contentCenterX + (display.contentCenterX * (e.xGravity*3))
	-- Borders
	if((player.x - player.width * 0.5) < 0) then
		player.x = player.width * 0.5
	elseif((player.x + player.width * 0.5) > display.contentWidth) then
		player.x = display.contentWidth - player.width * 0.5
	end
end
function addBlock()
	local r = math.floor(math.random() * 4)
	if(r ~= 0) then
		local block = display.newImage('block.png')
		block.x = math.random() * (display.contentWidth - (block.width * 0.5))
		block.y = display.contentHeight + block.height
		physics.addBody(block, {density = 1, bounce = 0})
		block.bodyType = 'static'
		blocks:insert(block)
	else
		local badBlock = display.newImage('badBlock.png')
		badBlock.name = 'bad'
		physics.addBody(badBlock, {density = 1, bounce = 0})
		badBlock.bodyType = 'static'
		badBlock.x = math.random() * (display.contentWidth - (badBlock.width * 0.5))
		badBlock.y = display.contentHeight + badBlock.height
		blocks:insert(badBlock)
	end
end
function addLive()
	live = display.newImage('live.png')
	live.name = 'live'
	live.x = blocks[blocks.numChildren - 1].x
	live.y = blocks[blocks.numChildren - 1].y - live.height
	physics.addBody(live, {density = 1, friction = 0, bounce = 0})
end
function gameListeners(action)
	if(action == 'add') then
		Runtime:addEventListener('accelerometer', movePlayer)
		Runtime:addEventListener('enterFrame', update)
		blockTimer = timer.performWithDelay(800, addBlock, 0)
		liveTimer = timer.performWithDelay(8000, addLive, 0)
		player:addEventListener('collision', collisionHandler)
	else
		Runtime:removeEventListener('accelerometer', movePlayer)
		Runtime:removeEventListener('enterFrame', update)
		timer.cancel(blockTimer)
		timer.cancel(liveTimer)
		blockTimer = nil
		liveTimer = nil
		player:removeEventListener('collision', collisionHandler)
	end
end

Next Time…

In the next and final part of the series, we’ll handle the blocks and player movement, collisions, 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!

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • Andrea

    Hi
    The simulator is black and not see anything.
    There are no errors.
    My corona is a Droid.
    Can you help me?
    Thanks

  • Jordan

    Thanks for the awesome tut, Carlos. Just wanted to point out that some of the function names in the beginning are different than what they are at the end in the code review. Other than that, everything works great (once completing the next section)!