Storyboard/Composer question

Hi, I’m new to corona and coding in general so please bear with me.

So far I’ve managed to create simple buttons, display images and things like that but have looked through all the resources and tutorials i could find both on this site and off it and am still confused as to how to switch between .lua files.

That is, how do i make it go from one .lua file to another .lua file.

From my limited understanding, the storyboard or composer API covers this, however so far I haven’t had any success with either.

Any help and example code would be nice.

(secondary question: is it necessary to delete objects, images, events etc created by previous .lua files when going to another .lua file. If so, how could i do this?)

Thanks in advance 

Hi @lazarfn,

Welcome to Corona! Composer is the official scene management library (Storyboard is officially deprecated), and while it takes a little bit of “up-front learning”, once you get accustomed to it, you’ll likely not use another method/ :slight_smile:

In regards to tutorials and guides, have you seen all of the resources outlined here?

https://coronalabs.com/blog/2015/08/11/tutorial-treasury-composer/
 

Hope this helps,

Brent

Welcome to corona SDK!!!

Try this!?

Main.lua

local composer = require( "composer" ) composer.gotoScene( "Menu" )

Menu.lua

local composer = require( "composer" ) local scene = composer.newScene() function scene:create( event ) local sceneGroup = self.view local BG = display.newRect(display.contentCenterX, display.contentCenterY, 1080, 1920) BG:setFillColor(1, 0, 0) end function scene:show( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "did" ) then local counter = 0 local counterTimer local counterTxt = display.newText( "0", 0, 0, native.systemFontBold, 40 ) counterTxt.x = display.contentCenterX counterTxt.y = 20 counterTxt:setFillColor(0, 0, 1) sceneGroup:insert(counterTxt) local function updateCounter(event) counter = counter + 1 counterTxt.text = string.format("%d", counter ) end local counterTimer = timer.performWithDelay( 1000, updateCounter, -1 ) end end scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) return scene

Ok so try that and you sound be set! 

Good Luck!

I tried directly copy and pasting the above code and it returns an error

module “menu.lua” not found

Also, from there, how would i navigate between .lua files? sorry if its a stupid question I’m genuinely confused.

As for the tutorials, yes I have looked through those resources as well as others on this website and havent had any success yet.

The code is supposed to work.

Can you add me on Skype? I can help you there?

RuvimKuz2000

Thanks! 

Please look at the code again and retry it… I made a stupid mistake and fixed it.

Thanks!

Im sorry im being soo lazy today and not checking my code haha.

Here is the redone and working code…

local composer = require( "composer" ) composer.gotoScene( "Menu" )

Menu.lua

local composer = require( "composer" ) local scene = composer.newScene() function scene:create( event ) local sceneGroup = self.view local BG = display.newRect(display.contentCenterX, display.contentCenterY, 1080, 1920) BG:setFillColor(1, 0, 0) sceneGroup:insert( BG ) end function scene:show( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "did" ) then local counter = 0 local counterTimer local counterText = display.newText( "0", display.contentCenterX, display.contentCenterX, native.systemFontBold, 70) counterText:setFillColor( 0, 0, 1 ) sceneGroup:insert(counterText) local function updateCounter(event) counter = counter + 1 counterText.text = string.format("%d", counter ) end local counterTimer = timer.performWithDelay( 1000, updateCounter, -1 ) end end scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) return scene

Good Luck!

Ok haha ONE more post and i think i have what you need…

Main.lua

local composer = require( "composer" ) composer.gotoScene( "Menu" )

Menu.lua

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(1, 0, 0) sceneGroup:insert( BG ) end function scene:show( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "did" ) then local counter = 0 local counterTimer local counterText = display.newText( "0", display.contentCenterX, display.contentCenterX, native.systemFontBold, 70) counterText:setFillColor( 0, 0, 1 ) sceneGroup:insert(counterText) local function updateCounter(event) counter = counter + 1 counterText.text = string.format("%d", counter ) end local counterTimer = timer.performWithDelay( 1000, updateCounter, -1 ) local function GotoGame(event) if event.phase == "ended" then timer.cancel(counterTimer) display.remove(counterText) composer.gotoScene( "game", { time = 1000, effect = crossFade }) end end 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 Game", onEvent = GotoGame } sceneGroup:insert(btn) end end scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) return scene

Game.lua

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 local counterTimer local counterText = display.newText( "100", display.contentCenterX, display.contentCenterX, native.systemFontBold, 70) counterText:setFillColor( 1, 0, 0 ) sceneGroup:insert(counterText) local function updateCounter(event) counter = counter - 1 counterText.text = string.format("%d", counter ) end local counterTimer = timer.performWithDelay( 1000, updateCounter, -1 ) local function GotoMenu(event) if event.phase == "ended" then timer.cancel(counterTimer) display.remove(counterText) composer.gotoScene( "Menu", { time = 1000, effect = crossFade }) end end 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

So this code lets you go from menu to game and vice versa… 

Good Luck!

Thanks its working now, just wonderingdo the counters serve any purpose?

I was mostly wondering how i could implement this into a button since 

local composer = require( “composer” )
composer.gotoScene( “Menu” )

seems to suffice with “composer.gotoscene( “menu” )” being used as an event, however when i try making that a function for a button it returns an error

i.e

display.setStatusBar( display.HiddenStatusBar )      

local composer = require(“composer”)

local widget =require (“widget”)

local function godeckbuilder( event )

if (“ended” == event.phase) then

composer.gotoScene( “deckbuilder” )

end

end

local deckbuilderbtn = widget.newButton

{

width = 100,

height = 50,

defaultFile = “icon.png”,

overFile = “icon.png”,

label = “deck builder”,

onEvent = godeckbuilder,

}

buttondeckbuilderbtn.x = 10

buttondeckbuilderbtn.y = 10

and lastly, could you point out which bit of code takes take of removing the previous scene’s buttons, background and things?

thanks a lot though you’ve been very helpful

for the record, im trying to make a simple card game

This should fix the error you gave me in your last post…

local composer = require("composer") local widget =require ("widget") local function godeckbuilder( event ) if ("ended" == event.phase) then composer.gotoScene( "deckbuilder" ) end end local deckbuilderbtn = widget.newButton { width = 100, height = 50, defaultFile = "icon.png", overFile = "icon.png", label = "deck builder", onEvent = godeckbuilder, } deckbuilderbtn.x = 10 deckbuilderbtn.y = 10

As for your other questions …

When you add an object to the sceneGroup like this

sceneGroup:insert(object)

Then when you change scenes the object removes itself…

But i dont know if this is a bug or what not but adding a newText to scene group to doesnt remove the obejct when changing scenes…

So i just do 

display.remove(object)

Now for canceling timers all you do is 

timer.cancel(timerVariable)

… 

You should look at “SCOPE” tuts for corona sdk… They are a little complicated i guess but it helps…

And like i mentioned before! You can always add me on Skype and id be glad to help out with any questions you might have!

Good Luck!

Hi, thanks for all your help so far and I’ve managed to get the first button working but the second button (returning to the menu) doesn’t seem to work and as far as i’m aware im using the same code except with different names for variables and functions.

here’s my code so far

Main.lua

display.setStatusBar( display.HiddenStatusBar )      

local composer = require(“composer”)

local widget =require (“widget”)

local function godeckbuilder( event )

if (“ended” == event.phase) then

composer.gotoScene( “deckbuilder” )

end

end

local deckbuilderbtn = widget.newButton

{

width = 100,

height = 50,

defaultFile = “icon.png”,

overFile = “icon.png”,

label = “deck builder”,

onEvent = godeckbuilder,

}

deckbuilderbtn.x = 10

deckbuilderbtn.y = 10

deckbuilder.lua

display.setStatusBar( display.HiddenStatusBar )      

local composer = require (“composer”)

local widget = require (“widget”)

local function gomenu( event )

if (“ended” == event.phase) then

composer.gotoScene( “main” )

end

end

local menubtn = widget.newButton

{

width = 100,

height = 50,

defaultFile = “icon.png”,

overFile = “icon.png”,

label = “menu”,

onEvent = gomenu,

}

menubtn.x = 10

menubtn.y = 10

and the error it gives me is

attempt to concatenate global ‘scenename’ (a nil value)

Hello again! I’m on mobile right now as I’m at work so sorry if I get something wrong… But what I think is wrong is This composer.gotoScene( “main” ) Has to be this composer.gotoScene( “Main” ) Because the file name is “Main.lua”… Lua is a very very case sensitive language. Good Luck!

I tried making that correction but it hasnt fixed the issue, also my “Deckbuilder” file has a capital d whereas the code for it does not so i dont think thats the issue. The rest of the code is copy pasted from the working one as i said its just the variable names changed.

Thanks for all the help its been very useful.

Can you please repost the code? But this time in the editor press the “<>” and add it in there…

Thanks!

I think i know the problem… After you require composer you have to add

local scene = composer.newScene()

In every file.

No luck so far with the local scene = composer.newscene()

the error appears as soon as it switches to the second scene, but before i click the button(i.e as soon as i press the first button and it loads the second scene (second button shows up)).

main.lua

display.setStatusBar( display.HiddenStatusBar ) local composer = require("composer") local widget =require ("widget") local function godeckbuilder( event ) if ("ended" == event.phase) then composer.gotoScene( "blank" ) end end local deckbuilderbtn = widget.newButton { width = 100, height = 50, defaultFile = "icon.png", overFile = "icon.png", label = "deck builder", onEvent = godeckbuilder, } deckbuilderbtn.x = 10 deckbuilderbtn.y = 10

deckbuilder.lua

display.setStatusBar( display.HiddenStatusBar ) local composer = require ("composer") local widget = require ("widget") local function gomenu( event ) if ("ended" == event.phase) then composer.gotoScene( "Main" ) end end local menubtn = widget.newButton { width = 100, height = 50, defaultFile = "icon.png", overFile = "icon.png", label = "menu", onEvent = gomenu, } menubtn.x = 10 menubtn.y = 10

On what line is it saying the error is in? And where is that line?

?:0: attempt to concatenate global ‘scenename’ (a nil value)

stack traceback:

?: in function ‘gotoScene’

main.lua:7 in function ‘_onEvent’

?: in function ‘?’

?: in function <?:677>

?: in function <?:221>

Is the error, it gives this after i press the first button and it loads the second scene (deckbuilder), but before i press the second button

I think this means the error is in main.lua but i think at this point it has left main.lua

Ok so since you are using composer you need to use the scenes… And you are making a game right? So if you dont use the scenes then you will have big problem later on when you start adding more code…

So here is a file of your problem(FIXED) 

Download it and unzip it and check out what i did…

http://www.mediafire.com/download/6y8b8z90xjj9q86/HELP.zip

Now ONLY use main.lua for stuff like loading files and stuff like that… And of course going to your first scene…

Corona does this…

main.lua --> Next Scene --> Next Scene and so on… 

Good Luck!

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