Some of the progress I made in the last days is the creation of an overlay menu which is triggered by the storyboard.overlay function of Corona SDK. When the user clicks on the pause button an overlay appears with 3 options: Pause the game, go to the level selection screen, and reload the level. In this post we will create this kind of overlay. See the video below for the implementation in my Rolly Bear World Project.
The overlay I used is a PNG file I created with InkScape. On the overlay I will position the buttons to reload, resume the game or to navigate to the level selection screen. The plain overlay file looks like this:
This menu is triggered when the user clicks the pause button on the top right of the game-screen.
Let’s start by creating this pause button in the createScene function in our level01.lua file:
local pauseBtn = display.newImageRect ("images/pausebutton.png", 112, 117) pauseBtn.y = 7+ (topScrn+ pauseBtn.height /2) pauseBtn.x = -7+ (withScrn - pauseBtn.width /2) pauseBtn.destination = "pauseBtn" pauseBtn:addEventListener("tap", btnTap) group:insert(pauseBtn) end
The pauseBtn triggers the btnTap function which we have created before. First we create the feeling of a touch by scaling the button down to 0.95%. Second we use storyboard.showOverlay to load a new scene in front of the existing scene. First we transition to a lua file called pauseoverlay (this is the lua file which contains the definition of our overlay), second we pass some parameters. Two are worth mentioning. the params = {levelNum = “level01″} pass on the level file we are currently in. As a result, we don’t need to create an overlay for each level, but we can re-use the overlay for each level in the future. Second isModal = true means that the user needs to handle the overlay first; before doing anything else. This does not allow the user to tap on any objects (like platforms) while the menu is still open.
local function btnTap(event) event.target.xScale = 0.95 event.target.yScale = 0.95 -- storyboard.showOverlay( "pauseoverlay" ,{effect = "fade" , params ={levelNum = "level01"}, isModal = true} ) return true end
Now create a new lua file and call it pauseoverlay.lua. This is the actual overlay scene we are creating.
The complete pauseoverlay.lua looks like this:
-- Rolly Bear World Project by Christian Peeters -- See all tutorial @christian.peeters.com local storyboard = require( "storyboard" ) local scene = storyboard.newScene() local physics = require("physics") local level01 = require("level01") local params -- local forward references should go here -- local function btnTap(event) event.target.xScale = 0.95 event.target.yScale = 0.95 storyboard.gotoScene ( event.target.destination, { params ={levelNum = params.levelNum}, time=800, effect = "fade"} ) return true end function catchBackgroundOverlay(event) return true end -- Called when the scene's view does not exist: function scene:createScene( event ) local group = self.view local backgroundOverlay = display.newRect (group, leftScrn-1000, topScrn-1000, withScrn+1000, heightScrn+1000) backgroundOverlay:setFillColor( black ) backgroundOverlay.alpha = 0.6 backgroundOverlay.isHitTestable = true backgroundOverlay:addEventListener ("tap", catchBackgroundOverlay) backgroundOverlay:addEventListener ("touch", catchBackgroundOverlay) local overlay = display.newImage ("images/overlayv2.png", 900 , 500) overlay.x = centerX overlay.y = centerY group:insert (overlay) local levelBtn = display.newImageRect ("images/levelBtn.png", 112, 116 ) levelBtn.x = centerX levelBtn.y = centerY + overlay.height/2.2 levelBtn.destination = "levels" levelBtn:addEventListener("tap", btnTap) group:insert(levelBtn) local playBtn = display.newImageRect ("images/playBtn.png", 112, 116) playBtn.x = centerX - overlay.width / 3 playBtn.y = centerY + overlay.height/2.2 local function hideOverlay(event) storyboard.hideOverlay("fade", 800) end playBtn:addEventListener ("tap", hideOverlay) group:insert(playBtn) local reloadBtn = display.newImageRect ("images/reloadbutton.png" ,112, 116) reloadBtn.x = centerX + overlay.width / 3 reloadBtn.y = centerY + overlay.height/2.2 params = event.params reloadBtn.destination = "reloading" reloadBtn:addEventListener ("tap", btnTap) group:insert (reloadBtn) 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
In line 32-34 we create the overlay based on the image displayed on top of this post.
In line 39-61 we create the 3 buttons for resuming the game, reloading the game and going to the level selection screen.
levelBtn has an eventlistener for btnTap, it pass the levelBtn.destination = “levels” into the storyboard.gotoscene which will load levels.lua.
playBtn has also an eventlistener but not for btnTap but for a local function hideOverlay. This simply hides the overlay in 800 milliseconds with a fade transition effect.
The reloadBtn is the tricky part. I did a lot of research online how to make this work properly. Reloading scenes is actually not an easy thing to do in Corona SDK and requires you to really understand how storyboard is build by Corona SDK. I really recommend to watch the video below before proceeding. This video is made by CoronaLabs.
As you can see in line 51 – 61, the reload.destination calls a lua file which does not exist yet, it calls reloading.lua. This is somehow a scene which is positioned between the current scene and between the reloaded scene. Here we remove the current scene and reload the scene completely again. By using the removeScene function Corona SDK will call the createScene again for us and position all the elements of the game back to its original position. While doing so we create a text on the screen for the user which informs the user of the reloading (“Getting Rolly Bear in position”). So add a new lua file to your project folder called reloading.lua (the code is listed below)
In line 15 of pauseoverlay.lua we call the storyboard.gotoScene and pass the event.target.destination (when the reloadBtn is touched this means that we pass on reloadBtn.destination = “reloading”, and will thus transition to reloading.lua.
Also note that in line 15 and 16 of reloading.lua we pass in again params.levelNum so we don’t need to re-create this reloading scene for each level. This has also been passed on from line 15 in pauseoverlay.lua
The reloading.lua file:
-- Rolly Bear World Project by Christian Peeters -- See all tutorial @christian.peeters.com -- external Moduals & libraries -- local storyboard = require( "storyboard" ) local _overlay = require( "gameoveroverlay" ) local _level01 = require("level01") local scene = storyboard.newScene() local loadText local params local function restartLevel (event) storyboard.removeScene(params.levelNum) storyboard.gotoScene (params.levelNum, {time=500, effect= "fade"}) end -- Called when the scene's view does not exist: function scene:createScene( event ) local group = self.view myStaticgroup = self.view params = event.params print(params.levelNum) loadText = display.newText("Getting RollyBear in Postion", 0, 0 , "Helvetica", 50) loadText.x = centerX loadText.y = centerY group:insert (loadText) end -- Called immediately after scene has moved onscreen: function scene:enterScene( event ) local group = self.view myStaticgroup = self.view loadText.alpha = 1.0 transition.to( loadText, {time=250, alpha = 0.0, onComplete=restartLevel} ) end -- Called when scene is about to move offscreen: function scene:exitScene( event ) -- 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 myStaticgroup = 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----------------------------------------------------------
There is one more thing you need to be aware of. When the overlay is positioned on the screen there is a bug in Corona SDK that elements which are not below the overlay can still be touched and moved. As my overlay doesn’t take over the complete screen, a user could still grab a platform and move it around. This is not desirable and can be fixed relatively simple. Create a rectangle as big as your screen and and add a “touch” and “tap” eventlistener which calls a function which only returns true (I called the function catchBackgroundOverlay). This means that anywhere were the user taps, it wont fall through to the underlying objects. I found this solution on the coronaLabs forum contributed by Rob Miracle. Great solution!
function catchBackgroundOverlay(event) return true end -- Called when the scene's view does not exist: function scene:createScene( event ) local group = self.view local backgroundOverlay = display.newRect (group, leftScrn-1000, topScrn-1000, withScrn+1000, heightScrn+1000) backgroundOverlay:setFillColor( black ) backgroundOverlay.alpha = 0.6 backgroundOverlay.isHitTestable = true backgroundOverlay:addEventListener ("tap", catchBackgroundOverlay) backgroundOverlay:addEventListener ("touch", catchBackgroundOverlay)
Hi Christian,
I hope your travels are going well – I really enjoy reading your posts about your travel experiences as well as tutorial on “Rolly Bear World”.
I’m fairly new to Corona SDK (and mobile application programming itself); therefore, your step-by-step tutorial means alot to me in regards to learning programming.
But to get to the point I hit a wall in this post – I have the overlay pause menu (and the latter) working perfectly except when I click either the “replay” button and level selection “menu” button I get a runtime error:
/level01.lua:201: attempt to perform arithmetic on field ‘x’ (a nil value)
in line 201 on level01.lua I have the following block of code that requests for the platforms with the spritesheet:
190 for x =1, #platformNames do
191 local platformNum = platformNames[x]
192 platform = display.newSprite( myPlatformSheet , {frames={sheetInfo:getFrameIndex(platformNum)}} )
193 platform.x = display.screenOriginX + 100
194 platform.y = 150 + 75 * x
195 totalnumPlatforms [#totalnumPlatforms+1] = platform
196 platform.platformNum= platformNum
197 physics.addBody( platform, physicsData:get(platformNum))
198 platform.bodyType = “static”
199 platform:addEventListener(“touch”, movePlatform)
200 group:insert(platform)
201 end
I’ve gone through the on level.lua, pauseoverlay.lua and reloading.lua and compared to your initial coding but can’t seem to figure out why I’m getting this error.
Your reply is very much appreciated!
Thanks!
JL