Can I recommend a few things to improve use of Composer? Composer is best when you do things in the right place and understand scope. In one of the code blocks agive, things are being created in “scene:show()” that are best done in “scene:create()” The techinque above appears to be done to avoid scope issues. First I’ll post the original code, adding some comments, then I’ll present what I recommend for the code.
local composer = require( "composer" ) local scene = composer.newScene() local widget = require("widget") function scene:create( event ) local sceneGroup = self.view local BG = display.newRect(display.contentCenterX, display.contentCenterY, 1080, 1920) BG:setFillColor(0, 0, 1) sceneGroup:insert( BG ) end function scene:show( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "did" ) then local counter = 100 --\<---- should be local to the whole scene local counterTimer --\<---- should be local to the whole scene -- these next three lines should be in ":create()" but counterText should be forward declared at the top local counterText = display.newText( "100", display.contentCenterX, display.contentCenterX, native.systemFontBold, 70) counterText:setFillColor( 1, 0, 0 ) sceneGroup:insert(counterText) -- best placed outside of this funciton to avoid nested functions. local function updateCounter(event) counter = counter - 1 counterText.text = string.format("%d", counter ) end local counterTimer = timer.performWithDelay( 1000, updateCounter, -1 ) -- again move out side. local function GotoMenu(event) if event.phase == "ended" then timer.cancel(counterTimer) display.remove(counterText) composer.gotoScene( "Menu", { time = 1000, effect = crossFade }) end end -- should be in :create() local btn = widget.newButton { top = display.contentCenterY, left = display.contentCenterX - 80, width = 84, height = 105, font = native.systemFontBold, id = "button1", label = "Press To Go To Menu", onEvent = GotoMenu } sceneGroup:insert(btn) end end scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) return scene
This is what I would do:
local composer = require( "composer" ) local scene = composer.newScene() local widget = require("widget") -- forward declarations local counter = 100 local counterTimer local counterText local function updateCounter(event) counter = counter - 1 counterText.text = string.format("%d", counter ) end local function gotoMenu(event) --\<---- try and maintain camel case variables if event.phase == "ended" then timer.cancel(counterTimer) display.remove(counterText) composer.gotoScene( "menu", { time = 1000, effect = crossFade }) end end function scene:create( event ) local sceneGroup = self.view local BG = display.newRect(display.contentCenterX, display.contentCenterY, 1080, 1920) BG:setFillColor(0, 0, 1) sceneGroup:insert( BG ) counterText = display.newText( "100", display.contentCenterX, display.contentCenterX, native.systemFontBold, 70) counterText:setFillColor( 1, 0, 0 ) sceneGroup:insert(counterText) local btn = widget.newButton { top = display.contentCenterY, left = display.contentCenterX - 80, width = 84, height = 105, font = native.systemFontBold, id = "button1", label = "Press To Go To Menu", onEvent = gotoMenu --\<------ camel case variable/function names } sceneGroup:insert(btn) end function scene:show( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "did" ) then counterTimer = timer.performWithDelay( 1000, updateCounter, -1 ) elseif ( phase == "will" ) then -- initialize things here that need to be reset each time the scene loads. counter = 100 end end function scene:hide( event ) local phase = event.phase if ( phase == "will" ) then if counterTimer then timer.cancel( counterTimer ) counterTimer = nil end end end scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) return scene
Let me explain a few things. Composer is designed to create a scene once, and as long as you don’t hit a low memory situtation, the next time you access the scene it should not have to be re-created. Static things like displaying the counter and the button don’t change each time the scene loads. Anything like this should be created in scene:create().
The only thing scene:show() should do is reset any values that may change during the scene’s run that need reset back to their beginning values (in this case “counter”) during the “will” phase and then during the “did” phase start up anything that needs to start after the scene is on the screen. This includes physics, timers, transitions. You would hate for objects to already be falling before the scene comes fully on the screen. In this case, you have a timer.
scene:show() will always be paird with a scene:hide(). You are guarenteed to get these every time a scene loads and a scene exits. This is why you do these “starting” things here. And anything you start, you should attempt to top in scene:hide(). The scene:create() event is not guarenteed to fire each time the scene is entered (since it’s supposidly already created when you re-enter the scene). You can’t depend on it to start things.
Scope is important when working with composer. In this case, you create things inside of scene:create(), but scene:show() may need to access the object. Other functions may need to as well. When this is the case you declare the variable/function at the highest code block where it’s visible to all other functions that needs it. This is why I moved counter, counterTimer, counterText and such to the main chunk of the scene. Now any composer event can access them.
Nested functions (functions inside of other functions, or functions inside of if statements inside of functions) are harder to trouble shoot. It’s like programming yourself into a box (literally) where all you can see is the inside of the box. In some cases this makes sense, but in most cases like this, they don’t make sense to bury inside of an if statement inside of scene:show(). I’ve moved these functions to the main chunk. Now everything is visible where it’s needed.
Finally a couple of minor nit picks. Most people prefer camelCase where the first letter is lower case, and subsequent word is capitalized. Of course you can code your variables in any style you want, but more Lua developers use camelCase. But in this case, many things are camelCase, but the one variable was not. Consistancy matters.
This leads into the final thing, it’s best to keep filenames all lower case where possible. You should avoid Menu.lua and use menu.lua where you can. Devices are case sensitive and the simulator is not. It’s too easy to get into the “it works on the simulator but not on the device” when scene names and filenames don’t maintain a predicable standard, which for most people, its using lower case file names.
Sorry to hijack the main thread, but it’s good when we can all have consistant usage. Now back to the real issue.
Rob