Level Selection Template for Corona SDK

In the last couple of days I have tried to create some kind of template for level selection for games build with Corona SDK. This template is a level selection which you often see in mobile games. The user can select a level based on a grid outline and based on the status of the level the grid changes (open, locked or completed).

In the video below you can see what the template is.


How is the template build? I used storyboard to create the different scenes. We will have main.lua to launch the menu.lua. From the menu you can navigate to either gamecredits.lua or play.lua. Gamecredits.lua is empty as it has no purpose for this tutorial so there is only a back button. When you enter play.lua you get the grid with different levels. After selecting a level you enter level01.lua or level02.lua. I only have added level01.lua and level02.lua to the sample code, but it should be clear that you need a lua file for each level.

So lets start with main.lua. Main.lua simple starts the menu scene with a slideDown animation. Note that there are also two variable for housekeeping to determine the center of the screen.

local storyboard = require ("storyboard")
storyboard.purgeOnSceneChange = true

display.setStatusBar(display.HiddenStatusBar)

centerX = display.contentCenterX
centerY = display.contentCenterY

storyboard.gotoScene ( "menu", { effect = "slideDown"} )

Next we need to launch the menu.lua, which has 2 texts fields positioned to navigate either to play.lua or to the gamecredits.lua

local storyboard = require( "storyboard" )
local scene = storyboard.newScene()

-- local forward references should go here --

local function buttonHit(event)
	storyboard.gotoScene (  event.target.destination, {effect = "slideDown"} )
	return true
end

-- Called when the scene's view does not exist:
function scene:createScene( event )
	local group = self.view

	
	local title = display.newText( "Welcome to Game", 0, 0, "Helvetica", 38 )
	title.x = centerX
	title.y = display.screenOriginY + 40
	group:insert(title)
	
	local playBtn = display.newText(  "Start game", 0, 0, "Helvetica", 25 )
	playBtn.x = centerX
	playBtn.y = centerY
	playBtn.destination = "play" 
	playBtn:addEventListener("tap", buttonHit)
	group:insert(playBtn)
	
	local creditsBtn = display.newText(  "Credits", 0, 0, "Helvetica", 25 )
	creditsBtn.x = centerX
	creditsBtn.y = centerY + 80 
	creditsBtn.destination = "gamecredits" 
	creditsBtn:addEventListener("tap", buttonHit)
	group:insert (creditsBtn)
	
end


-- Called immediately after scene has moved onscreen:
function scene:enterScene( event )
	local group = self.view

	-- INSERT code here (e.g. start timers, load audio, start listeners, etc.)

end


-- Called when scene is about to move offscreen:
function scene:exitScene( event )
	local group = self.view

	-- INSERT code here (e.g. stop timers, remove listeners, unload sounds, etc.)
	-- Remove listeners attached to the Runtime, timers, transitions, audio tracks

end


-- Called prior to the removal of scene's "view" (display group)
function scene:destroyScene( event )
	local group = self.view

	-- INSERT code here (e.g. remove listeners, widgets, save state, etc.)
	-- Remove listeners attached to the Runtime, timers, transitions, audio tracks

end



---------------------------------------------------------------------------------
-- END OF YOUR IMPLEMENTATION
---------------------------------------------------------------------------------

-- "createScene" event is dispatched if scene's view does not exist
scene:addEventListener( "createScene", scene )

-- "enterScene" event is dispatched whenever scene transition has finished
scene:addEventListener( "enterScene", scene )

-- "exitScene" event is dispatched before next scene's transition begins
scene:addEventListener( "exitScene", scene )

-- "destroyScene" event is dispatched before view is unloaded, which can be
-- automatically unloaded in low memory situations, or explicitly via a call to
-- storyboard.purgeScene() or storyboard.removeScene().
scene:addEventListener( "destroyScene", scene )


---------------------------------------------------------------------------------

return scene

Now create the gameCredits.lua and set a title and a back button (we don’t want to get stuck).

local storyboard = require( "storyboard" )
local scene = storyboard.newScene()

-- local forward references should go here --
local function buttonHit(event)
	storyboard.gotoScene (  event.target.destination, {effect = "slideUp"} )
	return true
end


-- Called when the scene's view does not exist:
function scene:createScene( event )
	local group = self.view

	-- CREATE display objects and add them to 'group' here.
	-- Example use-case: Restore 'group' from previously saved state.
	
	local title = display.newText( "Game Credits", 0, 0, "Helvetica", 38 )
	title.x = centerX
	title.y = display.screenOriginY + 40
	group:insert(title)
	
	local backBtn = display.newText(  "Back", 0, 0, "Helvetica", 25 )
	backBtn.x = centerX
	backBtn.y = centerY
	backBtn.destination = "menu" 
	backBtn:addEventListener("tap", buttonHit)
	group:insert(backBtn)


end


-- Called immediately after scene has moved onscreen:
function scene:enterScene( event )
	local group = self.view

	-- INSERT code here (e.g. start timers, load audio, start listeners, etc.)

end


-- Called when scene is about to move offscreen:
function scene:exitScene( event )
	local group = self.view

	-- INSERT code here (e.g. stop timers, remove listeners, unload sounds, etc.)
	-- Remove listeners attached to the Runtime, timers, transitions, audio tracks

end


-- Called prior to the removal of scene's "view" (display group)
function scene:destroyScene( event )
	local group = self.view

	-- INSERT code here (e.g. remove listeners, widgets, save state, etc.)
	-- Remove listeners attached to the Runtime, timers, transitions, audio tracks

end



---------------------------------------------------------------------------------
-- END OF YOUR IMPLEMENTATION
---------------------------------------------------------------------------------

-- "createScene" event is dispatched if scene's view does not exist
scene:addEventListener( "createScene", scene )

-- "enterScene" event is dispatched whenever scene transition has finished
scene:addEventListener( "enterScene", scene )

-- "exitScene" event is dispatched before next scene's transition begins
scene:addEventListener( "exitScene", scene )

-- "destroyScene" event is dispatched before view is unloaded, which can be
-- automatically unloaded in low memory situations, or explicitly via a call to
-- storyboard.purgeScene() or storyboard.removeScene().
scene:addEventListener( "destroyScene", scene )


---------------------------------------------------------------------------------

return scene

Up till now we only build the storyboard which is standard stuff from the Corona SDK. In play.lua we will actually build the level grid and build the logic for handling the different statuses like open level, locked level, or completed level.

- We use level.png for an open level
- We use lock.png for a locked level
- We use greenchecked.png for a level which is completed.
You can find these PNG files on the bottom of this post in a zip-file

The code block below is your play.lua. Below the code block I explain some of the most important elements

local storyboard = require( "storyboard" )
local scene = storyboard.newScene()


-- local forward references should go here --

levels = 
{	
1, 2, 2, 2 , 2,  --1 means level is open to 	be played (level.png)
2, 2, 2, 2, 2,   --2 means level is locked (locked.png)
2, 2, 2, 2, 2   -- 3 means level is completed (greenchecked.png)
}
	
images ={
	{ getFile = "level.png", types = "play"   },
	{ getFile = "lock.png", types = "locked"},
	{ getFile = "greenchecked.png", types = "done"}
}


local function buttonHit(event)
	storyboard.gotoScene ( event.target.destination, {effect = "slideUp"} )	
	print( event.target.destination)
		return true
end

-- Called when the scene's view does not exist:
function scene:createScene( event )
	local group = self.view
	
	local levelIndex =0
		for i=0,2 do
			for j=1,5 do
				tablePlace =   i*5 + j	
				levelIndex = levelIndex + 1
					local imagesId = levels[levelIndex] 
						levelImg = display.newImageRect (images[imagesId].getFile , 45, 45 )
						levelImg.x = 45 + (j*65)
						levelImg.y  = 85+ (i*65)
						group:insert(levelImg)

						leveltxt = display.newText("Level "..tostring(tablePlace), 0,0, "Helvetica", 10)
						leveltxt.x = 45 + (j*65)
						leveltxt .y = 110+ (i*65)
						leveltxt:setTextColor (250, 255, 251)
						group:insert (leveltxt)
						
					    levelImg.destination = "level0"..tostring(tablePlace)
						
						if images[imagesId].types ~= "locked" then
						levelImg:addEventListener("tap", buttonHit)
						end
 end
	
end
		


	
	-- CREATE display objects and add them to 'group' here.
	-- Example use-case: Restore 'group' from previously saved state.
	
	local title = display.newText( "Level Selection", 0, 0, "Helvetica", 20 )
	title.x = centerX
	title.y = display.screenOriginY + 40
	group:insert(title)
	
	local backBtn = display.newText(  "Back", 0, 0, "Helvetica", 20 )
	backBtn.x = display.screenOriginX + 50
	backBtn.y = display.contentHeight  - 30 
	backBtn.destination = "menu" 
	backBtn:addEventListener("tap", buttonHit)
	group:insert(backBtn)



end


-- Called immediately after scene has moved onscreen:
function scene:enterScene( event )
	local group = self.view

	-- INSERT code here (e.g. start timers, load audio, start listeners, etc.)

end


-- Called when scene is about to move offscreen:
function scene:exitScene( event )
	local group = self.view

	-- INSERT code here (e.g. stop timers, remove listeners, unload sounds, etc.)
	-- Remove listeners attached to the Runtime, timers, transitions, audio tracks

end


-- Called prior to the removal of scene's "view" (display group)
function scene:destroyScene( event )
	local group = self.view

	-- INSERT code here (e.g. remove listeners, widgets, save state, etc.)
	-- Remove listeners attached to the Runtime, timers, transitions, audio tracks

end



---------------------------------------------------------------------------------
-- END OF YOUR IMPLEMENTATION
---------------------------------------------------------------------------------

-- "createScene" event is dispatched if scene's view does not exist
scene:addEventListener( "createScene", scene )

-- "enterScene" event is dispatched whenever scene transition has finished
scene:addEventListener( "enterScene", scene )

-- "exitScene" event is dispatched before next scene's transition begins
scene:addEventListener( "exitScene", scene )

-- "destroyScene" event is dispatched before view is unloaded, which can be
-- automatically unloaded in low memory situations, or explicitly via a call to
-- storyboard.purgeScene() or storyboard.removeScene().
scene:addEventListener( "destroyScene", scene )


---------------------------------------------------------------------------------

return scene
	

The levels table in line 10-15 holds the status of the levels. A level can have either the status: open, locked or completed. 1 meaning open, 2 locked and 3 completed. When we launch play.lua for the first time only level one is open. When you for example want the first three levels to be open, then set the first 3 values in the table as 1.

In line 17 till 21 you can see a table with the images belonging to one of the 3 statuses. Each element in the table has two values. The first one is the actual PNG file (getFile) and the second one is the type. We want a type element so we can make easily a reference later on.

Line 33- 55 is the more exciting stuff :). In this block we line up 15 levels in one screen by positioning 5 levels horizontal and 3 levels vertical. We create these by a for-loop. Later one we also want a level number text below the level so the user can read the level number. This is hold in the variable tablePlace and is later concatenated in levelText. When we loop through we want to hold a variable of the level called levelIndex at start we set this to zero so we always have only 15 levels in total. This levelIndex value is used to get the correct PNG file for the level. In imagesId = levels[levelIndex] we take our levelIndex and get the corresponding table value of levels. Then we create the level image (levelImg) by using imagesId to get our actual PNG levelImg = display.newImageRect (images[imagesId].getFile , 45, 45 ). We use levelImg.x and levelImg.y to position the each level in the grid. We also position below each level image a text of the level For example level 1, level 2, level 3 etc).

levelImg.destination = “level0″..tostring(tablePlace) creates the variable so we know which level.lua we need to open. You have to watch out here. As we concatenate tablePlace to “level0″ and tablePlace generates a number between 1 and 15. This means to make it work your level lua files MUST BE NAMED as level01.lua, level02.lua etc.

In the last part we have an if-statement which allows the user only to click on a level which is not locked (~=). You can see that we use the types element of the levels table to check for this.

Now open your level01.lua (the code below is the same for level02.lua, level03.lua etc). So for this example I only show level01.lua.

local storyboard = require( "storyboard" )
local scene = storyboard.newScene()

local widget = require ("widget")
local playfile = require ("play")


-- local forward references should go here --

local completegameBtn


local function buttonHit(event)
	storyboard.gotoScene (  event.target.destination, {effect = "slideUp"} )
	return true
end


-- Called when the scene's view does not exist:
function scene:createScene( event )
	local group = self.view

	-- CREATE display objects and add them to 'group' here.
	-- Example use-case: Restore 'group' from previously saved state.
	
	local title = display.newText( "Level 1", 0, 0, "Helvetica", 38 )
	title.x = centerX
	title.y = display.screenOriginY + 40
	group:insert(title)
	
	local backBtn = display.newText(  "Back", 0, 0, "Helvetica", 25 )
	backBtn.x = display.screenOriginX + 50
	backBtn.y = display.contentHeight - 30 
	backBtn.destination = "play" 
	backBtn:addEventListener("tap", buttonHit)
	group:insert(backBtn)

	
	
end


-- Called immediately after scene has moved onscreen:
function scene:enterScene( event )
	local group = self.view

	-- INSERT code here (e.g. start timers, load audio, start listeners, etc.)
	
	local function btnClicked (event)
	levels[1] = 3
	levels[2] = 1
	completegameBtn.destination = "play" 
	completegameBtn:addEventListener("tap", buttonHit)
	end
	
	
	completegameBtn = widget.newButton { label = "Complete game", onRelease=btnClicked}
	completegameBtn.x = centerX
	completegameBtn.y = centerY
	group:insert (completegameBtn)

end


-- Called when scene is about to move offscreen:
function scene:exitScene( event )
	local group = self.view

	-- INSERT code here (e.g. stop timers, remove listeners, unload sounds, etc.)
	-- Remove listeners attached to the Runtime, timers, transitions, audio tracks

end


-- Called prior to the removal of scene's "view" (display group)
function scene:destroyScene( event )
	local group = self.view

	-- INSERT code here (e.g. remove listeners, widgets, save state, etc.)
	-- Remove listeners attached to the Runtime, timers, transitions, audio tracks

end



---------------------------------------------------------------------------------
-- END OF YOUR IMPLEMENTATION
---------------------------------------------------------------------------------

-- "createScene" event is dispatched if scene's view does not exist
scene:addEventListener( "createScene", scene )

-- "enterScene" event is dispatched whenever scene transition has finished
scene:addEventListener( "enterScene", scene )

-- "exitScene" event is dispatched before next scene's transition begins
scene:addEventListener( "exitScene", scene )

-- "destroyScene" event is dispatched before view is unloaded, which can be
-- automatically unloaded in low memory situations, or explicitly via a call to
-- storyboard.purgeScene() or storyboard.removeScene().
scene:addEventListener( "destroyScene", scene )


---------------------------------------------------------------------------------

return scene

In level01.lua we create a button, which completes the level when the user clicks it. (As you can see its a very difficult game….)

So when a user completes level 1 (levels[1]) we set the value in the levels table to 3. (which means the greenchecked.png). But we also set levels[2] as 1, which means level.png (level is open). For each lua level file you need to set this.

You can download the complete source code here.

In case you want to save the state of the level, so that users keep their progress when they close the app (game). Read this tutorial.

28 Responses

  1. Sean September 22, 2013 / 1:38 am

    If I wish to have more than 15 levels, would I change the for-loop

    • Christian September 23, 2013 / 1:51 pm

      Yes change the for-loop

      for i=0,2 do
      for j=1,5 do

      i=0,2 are three rows (0, 1, 2). you can add easily 1 by making it i=0,3. Same for the columns j = 1, 5.

      You probably get some space issues. You could add a slider widget (part of the Corona SDK

      • Anonymous September 23, 2013 / 7:25 pm

        Thank You, and I tried exactly what you had said but I kept getting an error. I used i=0,3
        and j=0,6. Am I supposed to change the equation below involving tablePlace = i*5 + j

      • Sean September 23, 2013 / 7:37 pm

        Thank You, and I tried exactly what you had said but I kept getting an error. I used i=0,3
        and j=0,6. Am I supposed to change the equation below involving tablePlace = i*5 + j

        • Christian September 25, 2013 / 12:44 pm

          Hi Sean,

          Sorry for that, I wasnt complete. I tested the following and it is working for me.

          Update the for loop with:
          for i=0,3 do
          for j=1,6 do

          but what was missing was the update of the level table

          You need to add an extra column and row as well.
          levels =
          {
          1, 2, 2, 2 , 2, 2, –1 means level is open to be played (level.png)
          2, 2, 2, 2, 2, 2, –2 means level is locked (locked.png)
          2, 2, 2, 2, 2 , 2, — 3 means level is completed (greenchecked.png)
          2, 2, 2, 2, 2 , 2
          }

          Hope this helps

    • Marc March 21, 2014 / 10:32 pm

      Awesome! Thank you!

  2. Sean September 28, 2013 / 10:02 pm

    I did that but I received an error:( Attempt to index global ‘levelImg’ (a nil value) )
    Could the problem be that i am using text wrangler

    • Christian September 29, 2013 / 12:21 pm

      Hi Sean,

      No it shouldnt matter. Where exactly is it going wrong? Does the level layout load? Do you get the error directly after clicking one of the levels?

      If you like please copy and paste your code from the createScene function

  3. Sean September 29, 2013 / 10:29 pm

    I get the error after clicking the start game button. Then the simulator stops before the level menu and displays the error

  4. Sean September 30, 2013 / 4:21 am

    local storyboard = require( “storyboard” )
    local scene = storyboard.newScene()

    – local forward references should go here –

    –levelProgress = 0
    –levelImg = {}
    –allLevels = {}

    levels =
    {
    1, 2, 2, 2, 2, 2, –1 means level is open to be played (level.png)
    2, 2, 2, 2, 2, 2, –2 means level is locked (locked.png)
    2, 2, 2, 2, 2, 2, — 3 means level is completed (greenchecked.png)
    2, 2, 2, 2, 2, 2,
    }

    images ={
    { getFile = “level.png”, types = “play” },
    { getFile = “lock.png”, types = “locked”},
    { getFile = “greenchecked.png”, types = “done”}
    }

    local function buttonHit(event)
    storyboard.gotoScene ( event.target.destination, {effect = “slideUp”} )
    print( event.target.destination)
    return true
    end

    – Called when the scene’s view does not exist:
    function scene:createScene( event )
    local group = self.view

    local levelIndex =0
    for i=0,3 do
    for j=1,6 do
    tablePlace = i*5 + j
    levelIndex = levelIndex + 1
    local imagesId = levels[levelIndex]
    levelImg = display.newImageRect (images[imagesId].getFile , 40, 40 )
    levelImg.x = 45 + (j*65)
    levelImg.y = 85 + (i*65)
    group:insert(levelImg)

    leveltxt = display.newText(tostring(tablePlace), 0,0, “TEN O CLOCK”, 25 )
    leveltxt.x = 45 + (j*65)
    leveltxt .y = 110 + (i*65)
    leveltxt:setTextColor (250, 255, 251)
    group:insert (leveltxt)

    levelImg.destination = “level0″..tostring(tablePlace)

    if images[imagesId].types ~= “locked” then
    levelImg:addEventListener(“tap”, buttonHit)
    end

    end

    end

    – CREATE display objects and add them to ‘group’ here.
    – Example use-case: Restore ‘group’ from previously saved state.

    local title = display.newText( “Level Selection”, 0, 0, “TEN O CLOCK”, 30 )
    title.x = centerX
    title.y = display.screenOriginY + 30
    group:insert(title)

    local backBtn = display.newText( “Back”, 0, 0, “TEN O CLOCK”, 30 )
    backBtn.x = display.screenOriginX + 50
    backBtn.y = display.contentHeight – 30
    backBtn.destination = “menu”
    backBtn:addEventListener(“tap”, buttonHit)
    group:insert(backBtn)

    end

    – Called immediately after scene has moved onscreen:
    function scene:enterScene( event )
    local group = self.view

    – INSERT code here (e.g. start timers, load audio, start listeners, etc.)

    end

    – Called when scene is about to move offscreen:
    function scene:exitScene( event )
    local group = self.view

    – INSERT code here (e.g. stop timers, remove listeners, unload sounds, etc.)
    – Remove listeners attached to the Runtime, timers, transitions, audio tracks

    end

    – Called prior to the removal of scene’s “view” (display group)
    function scene:destroyScene( event )
    local group = self.view

    – INSERT code here (e.g. remove listeners, widgets, save state, etc.)
    – Remove listeners attached to the Runtime, timers, transitions, audio tracks

    end

    ———————————————————————————
    – END OF YOUR IMPLEMENTATION
    ———————————————————————————

    – “createScene” event is dispatched if scene’s view does not exist
    scene:addEventListener( “createScene”, scene )

    – “enterScene” event is dispatched whenever scene transition has finished
    scene:addEventListener( “enterScene”, scene )

    – “exitScene” event is dispatched before next scene’s transition begins
    scene:addEventListener( “exitScene”, scene )

    – “destroyScene” event is dispatched before view is unloaded, which can be
    – automatically unloaded in low memory situations, or explicitly via a call to
    – storyboard.purgeScene() or storyboard.removeScene().
    scene:addEventListener( “destroyScene”, scene )

    ———————————————————————————

    return scene

    • Christian October 3, 2013 / 6:34 pm

      Hi Sean,
      Do you have the images in the same folder as your play.lua?

      The images array has no path for the images in my code, meaning that level.png, lock.png etc are on the same level as my lua files. If you have for example an images folder you need to define the path as well.

      Could this be the cause of your issue?

      images ={
      { getFile = “level.png”, types = “play” },
      { getFile = “lock.png”, types = “locked”},
      { getFile = “greenchecked.png”, types = “done”}
      }

  5. amar October 13, 2013 / 12:11 am

    Hey, can you give the simple saved state syntax in your tmplate. Coz every I close the app it have to play from the start.
    I mean except lvl 1 its locked.

    Btw its good template. Thanks

    • Christian October 14, 2013 / 12:27 am

      Hi Amar, thanks for visiting my blog. This is a very good point. I didn’t include it in the post. Let me update the post in the next 1-2 days and I will include it. I still have to make it myself but I have already an idea how to do it.

  6. eik November 7, 2013 / 9:51 pm

    Hi Amar,
    Thanks for sharing. Just wondering though… I notice that you are not removing event listeners created for levelImg and the buttons on exitscene…. Will this create memory leaks?

  7. eik November 7, 2013 / 9:52 pm

    I meant to Christian earlier.

  8. helios January 7, 2014 / 7:04 pm

    thnx for sharing! I’m reviewing this now. thanks for the code too!

  9. Daniel January 15, 2014 / 3:04 pm

    Perfect, exactly what I was looking for! Thanks!

  10. Marc February 15, 2014 / 8:19 pm

    Hi Christian,
    if I change “tap” with “collision”

    local function btnClicked (event)
    levels[1] = 3
    levels[2] = 1
    completegameBtn.destination = “play”
    completegameBtn:addEventListener(“tap”, buttonHit)
    end

    how I fix this?

    completegameBtn = widget.newButton { label = “Complete game”, onRelease=btnClicked}

  11. Marc February 17, 2014 / 4:10 pm

    I’ll explain, in your example, the level is completed when I click on “Complete Game” then returns to the screen play.lua and level 2 is unlocked …
    I would that the level is complete with the collision of two objects.
    could you explain how to change this thing please?

    • Christian February 21, 2014 / 10:13 am

      Hi Mark,

      Apologies for my late response, I had no Internet connection for a few days.

      It’s quite simple, you need to set up you collision function and when the object you want to collide happen you set the values of the levels table:

      function onCollision(event)

      if (event.object1.myName==”objectName” and event.object2.myName==”objectName”) then
      levels[1] = 3
      levels[2] = 1
      end

      Runtime:addEventListener(“collision”, onCollision)

  12. Marc February 21, 2014 / 6:49 pm

    okkk thank you very much Christian!!!

  13. John March 23, 2014 / 4:19 am

    Hello Christian,

    Thank you for this very easy to follow along storyboard tutorial. I’ve been surfing the web for ages in hopes of finding a very well explained tutorial of what storyboard is and how I would go about using it. I just want you to know that this post has exceeded my understanding of how to properly use storyboard, and I’d like to thank you for that. I’m going to go through some of your other tutorials now and hope to see many more in the future.

    Thanks!

  14. Mehmet May 6, 2014 / 2:14 pm

    Hi there, Very nice tutorial :) What about to use this for a map game ? I mean, the levels button will be put on a map, just like in Bubble Witch Saga ? Do you have an ideal ? Thank you :)

  15. Florian September 13, 2014 / 10:30 pm

    Hi, thank you very much for that tutorial, very helpful.

    Perhaps you have an idea what I am doing wrong while trying to use your code. I have sort of an Astroid clone and want the game to unlock the next level after a certain score was achieved.

    I am using the play.lua file as in your example.
    In my level01 file it says:
    local playfile = require (“play”)
    …..
    local levelcomplete
    …….
    local function game_over()

    if game_on then
    game_on = false
    local explosion = make_explosion()
    explosion.x = ship.x
    explosion.y = ship.y
    ship.isVisible = false
    timer.cancel( missile_timer )

    timer.performWithDelay( 1200, function()
    if score > 10 then
    levels[1] = 3
    levels[2] = 1
    levelcomplete.destination = “play”
    storyboard.showOverlay( “game_complete”, {effect=”fade”} )
    else
    storyboard.showOverlay( “game_over”, {effect=”fade”} )
    end
    end )
    end

    the game works great and everything does what it should do as long as you don’t achieve more than 10 points, otherwise I get this error:
    Corona Simulator Runtime error
    File: level01.lua
    Line: 252
    Attempt to index upvalue ‘levelcomplete’ (a nil value) —- this line: levelcomplete.destination = “play”

    stack traceback:
    level01.lua:252: in function ‘_listener’
    ?: in function
    ?: in function

    any help is appreciated. thank you

  16. johnny January 16, 2015 / 3:36 am

    Thank yu so much for this great example! It had really saved me from alot of headache. I am trying to make another layer this this by adding a WORLD selection, that has levels in that world to select from. I am having trouble saving the worlds array to the json file. I have little understanding of tables and table manipulation . can you point me in right direction to help me make this happen? so from main menu i want to select play. that should send me to a scene where i select the world. after selecting the world i should be send to another scene where i select a level in that world to play. and i want the save function as well

  17. valentine April 21, 2015 / 10:43 am

    Hi,

    Thanks for the great post. I was very helpful.

    Although, i am facing a particular challenge implementing your code.

    When i click on complete game on level01.lua, it takes me to play.lua but doesn’t unlock level02.lua.

    What am i doing wrong. kindly assist.

  18. Notorious Labs April 23, 2015 / 9:02 am

    Hello! How can we edit this to be compatible with Composer? Storyboard is no longer available with the new release of Corona SDK.

Leave a Reply to johnny Cancel reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

*