Creating Circle Pop – Part 5

Hello and welcome to the final Corona SDK tutorial in this game series! In this last tutorial, we will learn how to make the game run. We’ll learn how to save information to a file for high scores, to create dozens of objects using a timer, and to remove display objects by touching them. We’ll also learn some other cool tricks along the way!

To get started, create a file called game.lua in your project folder. If you’ve been following this series (Part 1, Part 2, Part 3, and Part 4), your project folder should have the following files.

  • build.settings
  • config.lua
  • game.lua (we just now created this file)
  • main.lua
  • mainmenu.lua

Since we are keeping this game simple, we really don’t need any other folders. If this was a bigger project, we would have folders for our music, sfx, graphics and so on.

Moving on, open game.lua in your favorite text editor. game.lua will be where all the action happens such as allowing the player to play the game. Our first step to building this scene will be to require in the storyboard module.

local storyboard = require( "storyboard" )

Next, we will load information from our saved file into a table. This way, we can track the number of popped circles over the lifetime of the game.

savedInfo = loadTable("savedInfo.json")

Then, we will declare some variables. If you’ve been reading the tutorial series, this should seem pretty familiar since we are following a pattern. For this scene, we will need to use some extra variables with our game file to make our game run. I’ve placed comments next to each variable to explain what each variable does.

local scene = storyboard.newScene() -- Used for Coronas storyboard

local numberOfCirclesPopped = savedInfo.circlesPopped -- This will track the number of popped circles and load the number of circles popped so far.
local numberOfCircles = 0 -- This will track the number of on screen circles.
local txt_circlesPopped = "" -- This is considered forward declaration. We declare variables before we use them so we can access them throughout the scene.
local circleCreationSpeed = 750 -- How fast the circles will be created in milliseconds
local maxNumberOfCircles = 50 -- How many circles to create

Since we want to add some life to our game, we will create a table that will be used to apply a gradient to our background rectangle. Feel free to change the colors as you see fit.

local backgroundGradient = graphics.newGradient(
  { 3, 88, 140 },
  { 100, 200, 200 },
  "down" )

After our gradient, we will create a function that will fire whenever a circle is touched within the game. In this function, we will add one point to the players score and remove the circle. We will also update the on screen text object with the latest score and make sure it stays centered.

Regarding the removal process, it’s important to remember that it’s always a bad idea to remove an object in the middle of a function. If the object is removed before the function is completed, you could introduce some unwelcome bugs or crash the game. For example, when I first started with Corona SDK, I used to remove the object and then try to compare the object to a value. When the comparison happened, my game would usually crash which is never a good thing!

Therefore, it’s always a good idea to delay the removal by a few milliseconds and that’s why the self:removeSelf() line is wrapped inside the timer.performWithDelay.

local function onCircleTouch(self, event)
if(event.phase == "began") then

  -- This next line will remove the circle from the screen. When removing objects within a touch event, its     important to never remove the object immediately. Always wait at least 1 cycle before removing the objects
timer.performWithDelay(1, function() self:removeSelf() end )

  numberOfCirclesPopped = numberOfCirclesPopped + 1 -- Increment our circles popped variables
  txt_circlesPopped.text = "Circles Popped: " .. numberOfCirclesPopped -- Update the text
  txt_circlesPopped.x = display.contentWidth*0.5 -- Make sure our text stays centered

  savedInfo.circlesPopped = numberOfCirclesPopped
  saveTable(savedInfo, "savedInfo.json")

  end

  return true -- Return true to signify that we have successfully executed a touch event.
end

Next, we will create a function called onGameOverTouch to allow our players to go back to the main menu. This function will only be called when the display object txt_endgame is touched. We will create the actual object later.

local function onGameOverTouch(event)
  storyboard.gotoScene( "mainmenu" )
  return true
end

Now that we’ve set up our touch functions, we will then move on to the scene:createScene function. The create scene function is where all of the images, objects, and graphics are created. Think of this function as the drawing/starting scene.

function scene:createScene( event )
  local group = self.view -- All of our display objects will be stored within the group object. This will allow Corona to clean up unused objects and free up some memory.

  numberOfCircles = 0

Then, we will create a background that fills up the entire screen. We will also apply our gradient and insert it into the group object.

local background = display.newRect(0,0,display.contentWidth,display.contentHeight)
  background:setFillColor( backgroundGradient )
  group:insert(background)

For our game, we’d like to keep track of the number of circles on screen. So, we put a text object in our scene.

local txt_circles = display.newText("Number of Circles: 0",0,0,native.systemFont, 16)
  txt_circles.x = display.contentWidth*0.5
  txt_circles.y = display.contentHeight - 16
  txt_circles:setTextColor(0,0,0)
  group:insert(txt_circles)

This next object is just like txt_circles, but this tracks the number of circles popped (or touched). Every time a circle is popped, the variable numberOfCirclesPopped will be increased by one and this text object will be updated to show the latest score.

txt_circlesPopped = display.newText("Circles Popped: " .. numberOfCirclesPopped,0,0,native.systemFont, 16)
  txt_circlesPopped.x = display.contentWidth*0.5
  group:insert(txt_circlesPopped)

This next text object will allow players to return to the menu so they may continue playing the game. This object will only appear after the game is over and we accomplish this by setting the alpha property of this object to 0.

txt_endGame = display.newText("Game Over! Tap For Menu.",0,0,260,150,native.systemFontBold,42)
  txt_endGame.x = display.contentWidth*0.5
  txt_endGame.y = display.contentHeight*0.5
  txt_endGame.alpha = 0 -- set the alpha to 0 : 0=invisible and 1=visible
  txt_endGame:addEventListener("touch",onGameOverTouch)
  group:insert(txt_endGame)

The next function will be used to create the circles on screen. I’ve randomized the size, location, and color of each circle using math.random to add some replay value. Later in this tutorial, we will make a timer that will call this function.

function createCircles()
  local circleSize = math.random(20,35) -- Size of the circle
  local xLocation = math.random(0,display.contentWidth) -- x location of the circle
  local yLocation = math.random(circleSize*2,display.contentHeight-(circleSize*2)) -- y location of the circle. We also subtract the size of the circle to make sure the circles do not cover our text.

Still inside the createCircles function, the next piece of code will create a circle, assign a random color, attach the touch listener and insert it into the group object.

local circle = display.newCircle(xLocation,yLocation,circleSize)
  circle:setFillColor( math.random(0,255),math.random(0,255),math.random(0,255) )
  circle.touch = onCircleTouch
  circle:addEventListener("touch", circle)
  group:insert(circle)

Every time a circle is created, we are going to update our variable numberOfCircles and update our text object txt_circles.

numberOfCircles = numberOfCircles + 1
txt_circles.text = "Number of Circles: " .. numberOfCircles
txt_circles.x = display.contentWidth*0.5

Once the game has reached the max number of circles (in our case, it’s 50), we will stop the timer and display the game over text object. Once the text object is displayed, the player can then return to the menu. There’s also a few extra ‘ends’ here because we have to end the function createCircles and createScene.

if(numberOfCircles >= maxNumberOfCircles) then
  timer.cancel(tmr_createCircles) -- cancel timer
  txt_endGame.alpha = 1 -- bring opacity up to 1
  txt_endGame:toFront() -- bring the text object up to the front
end

end

end

After we’ve completed the create scene function, we will next create the scene:enterScene function. In the enter scene function, we will want to start any of our timers or processes that will be used in our game. In our game, we only have one timer to start.

function scene:enterScene( event )
  local group = self.view

  -- Run a timer to start the circle creation process.
  tmr_createCircles = timer.performWithDelay(circleCreationSpeed, createCircles, maxNumberOfCircles)

end

Finally, run two event listeners to fire the functions of createScene and enterScene. We also have to return the variable scene in order for Corona’s storyboard to work.

scene:addEventListener( "createScene", scene )
scene:addEventListener( "enterScene", scene )

return scene

And that’s it to creating a simple game with Corona SDK! If you would like to purchase this as a template to support this website or to avoid having to retype all of this code, you can buy the template at thatssopanda.com/corona-sdk-templates/. If you have any questions for me, please let me know through the comments below! Thanks again for reading another tutorial for Corona SDK by That’s So Panda!


11 Comments
  1. A undesired behavior happens when you lose the game and restart the scene game. Did you notice?

    • Hello! Although your comment is very vague, I did find that I forgot a few lines in the scene:enterScene function of mainmenu.lua. I’ve updated the page to clear the last scene and you shouldn’t run into any issues.

      • Hey! great I did a purge scene in the function that call the “game scene” in mainmenu.lua

        local function onStartGameTouch(event)
        if(event.phase == “began”) then
        storyboard.purgeScene(“game”)
        storyboard.gotoScene( “game”,”flip” ) — switch to the game scene
        end
        return true — return true to signify a successful touch event.
        end

        sorry for the vague 😉

  2. Hey Spoggles no go with the game.

    I keed getting an error loading the game.lua file
    something is wrong with the function onStartGameTouch(event) on mainmenu.lua. I tried your way and @JAIME’s way and both gave me an error. I’ve double checked the code syntax and everything is fine.

    There seems to be an error in the storyboard.gotoScene() between both the mainmenu.lua and game.lua

    “game.lua syntax error near ‘storyboard’ stack traceback:…mainmenu(where the gotoscene line is)”

    • Hello, can you let me know what version of Corona you are using?

  3. if(numberOfCircles >= maxNumberOfCircles) then

    gives me error

    game.lua:66: ‘)’ exptected near ‘&’

    how to fix

    • For some reason, my WordPress install decided to covert the greater than sign and less than sign to their html entity equivalent. If you replace the html entities with the correct sign, the code will work. For example, & g t ; (without the spaces) is a greater than sign.

  4. oops sorry
    this
    if(numberOfCircles >= maxNumberOfCircles) then
    gives me error

    game.lua:66: ‘)’ exptected near ‘&’

    how to fix

    • For some reason, my WordPress install decided to covert the greater than sign and less than sign to their html entity equivalent. If you replace the html entities with the correct sign, the code will work. For example, & g t ; (without the spaces) is a greater than sign.

Leave a Reply