Common Storyboard API Questions
With 40+ comments (and counting!) on the blog post announcing our brand-new Storyboard API, it’s clear to see that we’ve definitely sparked your interest!
After monitoring the comments feed and answering your questions individually for a few days (in the comments feed, the forums, and via email), I began to see a common theme among most of the questions. Therefore, I decided a blog post addressing the most commonly asked Storyboard API questions would be very useful to many people.
This blog post should also serve as a resource for those “porting” their apps from Ricardo Rauber‘s Director Class, because while there are definite similarities between the two, there are some important differences that should be noted if you want to make a smooth transition (no pun intended).
Request: If you have questions—not comments—that are not in this blog post, please post your questions in this forum thread to help prevent the comments section from getting too bloated.
Storyboard FAQ
Before reading the questions below, I recommend reading the previous blog post and also becoming more familiar with the API or what you read below may otherwise be confusing.
Here’s a summary of all the questions (click to jump to a specific question):
- When exactly do the events get called?
- What is the Director-equivalent of the “new” function?
- What is the Director-equivalent of the “clean” function?
- Does Storyboard include transition effects?
- Can I specify a color for the “fade” effect?
- How do you transfer data/variables between scenes?
- Does Storyboard work with widgets?
- I can’t use Storyboard, how can I get it?
When exactly do the events get called?
The most common questions about the Storyboard API have to do with the timing of certain events, and also what conditions need to be met before specific events are dispatched. This is admittedly the most difficult part to grasp at first, but should all be very clear once it’s explained (especially if you’re familiar with other Corona APIs).
To clear this up, I’ll attempt to break down the order-of-events to illustrate exactly where to put what within your Storyboard scene event listeners. Please read carefully, and try to visualize what’s going on in the scenario described below:
- At the start of the app—in main.lua—user calls storyboard.gotoScene() to transition to “scene1″.
- Storyboard first checks to see if “scene1″ has already been loaded—it has not. Therefore, a “createScene” event for scene1 is dispatched, which will create a “view” property for scene1 (a display group which holds all of scene1′s display objects).
- Immediately after the “createScene” event is dispatched, Storyboard will dispatch an “enterScene” event for scene1 because the scene has just been entered (scene transition has just completed).
- Later—in scene1—the user calls storyboard.gotoScene() to load “scene2″. Before anything happens, an “exitScene” event is dispatched for scene1.
- Since scene2 doesn’t yet exist, a “createScene” event will be dispatched, followed by an “enterScene” event for scene2.
- Within scene2′s “enterScene” listener, the user calls storyboard.purgeScene() on scene1 to remove its “view” display group (to free up texture memory). Since purging doesn’t completely unload the scene, scene1 object is still loaded, but its display group (and display objects) are completely removed. Before anything happens to scene1, however, a “destroyScene” event is dispatched to scene1 (to give you time to remove Runtime listeners, widgets, unload audio, etc).
- From scene2, the user calls storyboard.gotoScene() to go BACK to scene1. First, an “exitScene” is dispatched for scene2.
- Storyboard then checks to see if scene1 exists—it does. Next, storyboard will check to see if scene1 still has a “view” property—it doesn’t because it was previously purged in scene2. Since no “view” property exists for scene1, a “createScene” event is once again dispatched for scene1, followed by an “enterScene” event.
- Once again—within scene1—storyboard.gotoScene() is called for scene2 and an “exitScene” event is immediately dispatched for scene1.
- Storyboard once again checks to see if scene2 exists—this time it does. Next, it’ll check for a “view” property attached to scene2—it exists also (since it was never purged at any point). Since nothing needs to be created, Storyboard skips the “createScene” event for scene2 and goes straight to an “enterScene” event.
- This time, instead of purging scene1, the user decides to call storyboard.removeScene() on scene1 within the “enterScene” event listener of scene2. The storyboard.removeScene() function will first purge the scene (remove it’s “view” display group), and then completely unload the scene to free all of its existing variables and properties from memory (and the module, if the scene is an external module).
- So before anything happens, a “destroyScene” event is dispatched to scene1, followed by the removal of it’s “view” display group, followed by the complete unloading of the scene1 object.
It’s a pretty long scenario, but it’s worth studying to see exactly when specific events are dispatched. Alternatively, if you want a visual example, I recommend taking a look at the Storyboard SampleCode (with the Terminal). The sample will print out exactly which events are being dispatched (as well as when scenes are purged).
The sample also serves as a great “test bed” for you to experiment. Try calling storyboard.purgeScene() and storyboard.removeScene() within the different listeners to see what happens. This kind of experimentation is the best way to learn.
What is the Director-equivalent of the “new” function?
In the Storyboard API, there are two events that correspond to the Director Class’ new() function: the “createScene” event, and the “enterScene” event.
As shown in the scenario I previously outlined, the “createScene” event will only be dispatched if the scene’s “view” property (display group) does not exist. A situation where a scene exists—but its view does not—would be if the scene had been purged at some point.
The “enterScene” event, however, is dispatched every-single-time storyboard.gotoScene() is called for that scene (even if “createScene” had been dispatched). The “enterScene” event is dispatched as soon as the scene transition is complete (if an effect is specified, after the effect has completed).
What is the Director-equivalent of the “clean” function?
In the Storyboard API, there are two events that correspond to the Director Class’ clean() function. Both events differ slightly, with one being called more often than the other, so I recommend you read closely.
The “exitScene” event, which is dispatched whenever a scene is transitioned-away from, is generally used to unload things such as Runtime listeners, audio assets, etc. which were loaded during the “enterScene” event.
The “destroyScene” event is similar to “exitScene”, but is only called just prior to the purging of a scene (which also includes the removal of a scene). Scene’s are not automatically purged during every scene transition. They are only purged if storyboard.purgeScene() or storyboard.removeScene() is called explicitly, or if the OS issues a “low memory” warning (in which case the least recently used scene will be purged).
What you should place in the “exitScene” and “destroyScene” event listeners is completely up to you, and how you programmed your app. As a rule of thumb, if you need a specific action to happen on every scene transition-out, use the “exitScene” listener. If you only need it to happen whenever the “view” display group is removed, use the “destroyScene” listener. You can also use both, and work out which listener will be taking care of what.
Does Storyboard include transition effects?
Yes. There are several transition effects that are available to use. Please see the storyboard.gotoScene() API reference page, in the parameters section (under “effect”) for a complete listing of the different transition effects that are available.
If you do not specify an effect, then no effect will be used (the transition will be instant). If you do specify an effect, there’s an additional (optional) argument that you can use to specify how long it should take to perform the transition effect (the default is 500 milliseconds).
Can I specify a color for the “fade” effect?
There is no option to explicitly set a color for the “fade” transition effect. However, the default is black simply because the default stage color is black (if no background image is over it).
If you want the “fade” effect to show a color, use display.setDefault() to change the stage color before you call storyboard.gotoScene(). Here’s an example of how to set it to red:
storyboard.gotoScene( "scene2", "fade" ) -- will fade red
How do you transfer data/variables between scenes?
One very confusing problem that is commonly asked about is how to transfer data between scenes without using global variables. Fortunately, Storyboard gives you a very easy solution to that problem: simply store your variables in the storyboard table!
Here’s an example:
main.lua
storyboard.testVar = "Hello World! - From main.lua"
storyboard.gotoScene( "scene1" )
scene1.lua
local scene = storyboard.newScene()
print( storyboard.testVar )
-- Terminal output: Hello World! - From main.lua
return scene
If you plan on transferring a lot of data, I recommend making ‘testVar’ (or whatever you name it) a table and store everything in that instead.
Does Storyboard work with widgets?
You should be fine using widgets with Storyboard, however, there are some known issues. When it comes to using the tableView, scrollView, pickerWheel, and possibly the slider widget in conjunction with storyboard, it is recommended you treat these widgets as you would native widgets, in that you cannot insert them into a group.
If some of your scenes have problems with any widgets (the tableView seems to be the most problematic), I recommend creating them on every “enterScene” event, and removing them on every “exitScene” event (so you don’t have to insert them into the “view” property of the scene). It is also recommended that you do not use effects when transitioning to and from scenes that use problematic widgets.
When using the tableView, scrollView, pickerWheel, and possibly the slider, you should avoid inserting them into the scene’s “view” group (which is why they should be created/removed during “enterScene” and “exitScene”. Buttons and tabBars should work fine.
Also, this applies to buttons and tabBars as well, you must manually remove widgets (because they are not removed automatically when their parent group is removed), so be sure you are manually removing the widgets at some point (either in your “exitScene” or “destroyScene” event listeners).
NOTE: We are working on fixing the issues with the problematic widgets.
I can’t use Storyboard, how can I get it?
At the moment, the Storyboard API is only available to subscribers via Corona Daily Builds. If you want to start using it right away, subscribe now to download the latest build!
Great post! This helped clear some stuff up for me, especially the event listener/functions flow you did first. Thanks, and looking forward to using this!
Thank You very much for this explanation CLASSE A Jon!
Regards,
Thanks for the run through – very helpful. I’ve been able to port most of my game from Director so far without too many issues.
can I use it to reload a scene ?
(clean memory,stop events,reset varibles…)
@Jason: Yes, if you call storyboard.gotoScene( “scene1″, “fade” ) while you are in scene1, then it will dispatch an “exitScene” event, and then transition to scene1 (again) and then dispatch an “enterScene” event.
A “createScene” event won’t be dispatched at this point, that is, unless you purge the scene first (before calling storyboard.gotoScene()).
Regarding Scene effects: It would be good if there was some way of having more control over the animation of them.
Right now the effects are very limited. Typical effects needed would be more ease-in / ease-out or the very common “bounce” in effects. This would make a huge difference to game quality.
And also to control the function for both current and new scene (a & b in this example)
1.Say adding the option to have a function controling the position/rotation/scale/fade to allow custom-coded transitions.
storyboard.gotoScene( sceneName [, aFunction, bFunction, effectTime] )
2. Another way instead of adding custom-coded transition effects for the scenes would be to not just allow the fading time but also the animation type. Say: easy-in/ease out, bouncy, bubble-motion, wobble etc.
More parameter control of each effecttyp in other words
ex:
storyboard.gotoScene( sceneName [, effect, aAnimType, bAnimType, effectTime] )
Sorry, forgot to mention.
Most of the time, you usaly want to let the scenes overlap on top och each other. A lot of the effect do not use the “(over original scene)” method. There would be much better to have a parameter setting if the scenes should “wait” for each other, overlap each other or push each other.
But because this API now is released. The simplest (and maybe only) way now is to add more effect types. Not the most elegant & flexible solution. But at least it would not disturb the already present API.
So more effectypes with (over original scenes and definately more transition types)
(why did you use the word “effect” instead of the more commonly used “transition” especially regarding scenes/storyboards?)
I think the FAQ misses a further important question:
What kind of Objekt do I have to remove manually?
Because some Objects are not removed automatically by putting them into the scene group. For example the newEmbossedText.
And it seems there is no way to remove a Mask of the scrollwiew widget. If I remove my scrollview like this:
display.remove( scrollView )
scrollView = nil
the mask still remains in memory. How to fix that?
Thanks
ok, the scrollView widet error was my fault. i made the scrollview local in my enterScene event :/ insted local in my scene file. Now the scrollview will removed completely.
Anyhow, a list of objects I have to remove manually would be nice.
Does anyone have hints for removing and restoring a tableView on scene change? My tableView appears correctly on first load (it is in the enterScene event) and is removed in the exitScene event; however, when visiting that scene again (via the tab bar), the tableView is not added appropriately. I receive an error that later in the code the tableView object is a nil value.
The storyboard API and the tableView API both seem very cool, too bad they don’t work together so well yet.
Hi everybody, just wanted to say that I fixed my problem. Thanks in general for this article, can it be added to the primary documentation to make it more searchable? (I couldn’t find it by searching the site!)
How is inneractive set-up with Storyboard. Prior to the Storyboard declaration (local storyboard = require “storyboard”) or after, when using ads in multiple scene’s?
Can I use storyboard in this way … I want to have a persistent background that goes between all levels. So basically my levels would be on top of my background that’s in main.lua. It seems that when you put the background image in main.lua, this sits on top of everything else on the display list. Any thoughts?
I have successfully worked around the problems with widgets in storyboard scenes as discussed above. My difficulty is that recreating my widgets for each scene means I have no caching of scenes as I had hoped to have from this api.
My questions are then:
1) Are there fixes in the works, soon?
2) Can I construct my tableviews and cache them myself.
3) If so, do I add and remove them to the view property to maintain the same ordering with respect to tabviews and masthead panels?
Thanks,
John
Dear sirs,
Is there a page curl effect in StoryBoard ?
Regards,
I am not understanding the use of the storyboard and managing memory. I have created a little sample app to test memory usage but the results are not what I am expecting. Could someone please help point out the error in my code or straighten out my misconceptions on how this should work?
Here is my sample app code:
———————————————–
– main.lua
———————————————–
display.setStatusBar( display.HiddenStatusBar )
collectgarbage()
local textMem = system.getInfo( “textureMemoryUsed” ) / 1000000
print( “—————————————————” )
print( “” )
print( “Initial Memory Values: main.lua” )
print( ” Memory Usage: ” .. collectgarbage(“count”) )
print( ” Texture Memory: ” .. textMem )
print( “” )
local storyboard = require ‘storyboard’
storyboard.gotoScene( “scene1″, “fade”, 400 )
———————————————–
– scene1.lua
———————————————–
local storyboard = require( “storyboard” )
local scene1 = storyboard.newScene()
function scene1:createScene( event )
local grScene1 = self.view
local dch = display.contentHeight
local dcw = display.contentWidth
light = display.newImage(“switchLightOn.png”)
light.x = dcw/2
light.y = dch/2
grScene1:insert(light)
function gotoScene2 (event)
storyboard.gotoScene( “scene2″, “fade”, 400 )
end
end
function scene1:enterScene( event )
local grScene1 = self.view
light:addEventListener(“tap”, gotoScene2)
collectgarbage()
local textMem = system.getInfo( “textureMemoryUsed” ) / 1000000
print( “Scene1 Memory Values: Light Switch Image” )
print( ” Memory Usage: ” .. collectgarbage(“count”) )
print( ” Texture Memory: ” .. textMem )
print( “” )
end
function scene1:exitScene( event )
local grScene1 = self.view
light:removeEventListener(“tap”, gotoScene2)
grScene1 = nil
end
function scene1:destroyScene( event )
local grScene1 = self.view
grScene1 = nil
end
scene1:addEventListener( “createScene”, scene1 )
scene1:addEventListener( “enterScene”, scene1 )
scene1:addEventListener( “exitScene”, scene1 )
scene1:addEventListener( “destroyScene”, scene1 )
return scene1
———————————————–
– scene2.lua
———————————————–
local storyboard = require( “storyboard” )
local scene2 = storyboard.newScene()
function scene2:createScene( event )
local grScene2 = self.view
local dch = display.contentHeight
local dcw = display.contentWidth
bird = display.newImage(“bird.png”)
bird.x = dcw/2
bird.y = dch/2
grScene2:insert(bird)
function gotoScene3 (event)
storyboard.gotoScene( “scene3″, “fade”, 400 )
end
end
function scene2:enterScene( event )
local grScene2 = self.view
bird:addEventListener(“tap”, gotoScene3)
collectgarbage()
local textMem = system.getInfo( “textureMemoryUsed” ) / 1000000
print( “Scene2 Memory Values: Bird Image” )
print( ” Memory Usage: ” .. collectgarbage(“count”) )
print( ” Texture Memory: ” .. textMem )
print( “” )
end
function scene2:exitScene( event )
local grScene2 = self.view
bird:removeEventListener(“tap”, gotoScene3)
grScene2 = nil
end
function scene2:destroyScene( event )
local grScene2 = self.view
grScene2 = nil
end
scene2:addEventListener( “createScene”, scene2 )
scene2:addEventListener( “enterScene”, scene2 )
scene2:addEventListener( “exitScene”, scene2 )
scene2:addEventListener( “destroyScene”, scene2 )
return scene2
———————————————–
– scene3.lua
———————————————–
local storyboard = require( “storyboard” )
local scene3 = storyboard.newScene()
function scene3:createScene( event )
local grScene3 = self.view
local dch = display.contentHeight
local dcw = display.contentWidth
bohr = display.newImage(“bohr.jpg”)
bohr.x = dcw/2
bohr.y = dch/2
bohr:scale(.4, .4)
grScene3:insert(bohr)
function gotoScene1 (event)
storyboard.gotoScene( “scene1″, “fade”, 400 )
end
end
function scene3:enterScene( event )
local grScene3 = self.view
bohr:addEventListener(“tap”, gotoScene1)
collectgarbage()
local textMem = system.getInfo( “textureMemoryUsed” ) / 1000000
print( “Scene3 Memory Values: Trading Card Image” )
print( ” Memory Usage: ” .. collectgarbage(“count”) )
print( ” Texture Memory: ” .. textMem )
print( “” )
end
function scene3:exitScene( event )
local grScene3 = self.view
bohr:removeEventListener(“tap”, gotoScene1)
grScene3 = nil
storyboard.removeAll()
end
function scene3:destroyScene( event )
local grScene3 = self.view
grScene3 = nil
end
scene3:addEventListener( “createScene”, scene3 )
scene3:addEventListener( “enterScene”, scene3 )
scene3:addEventListener( “exitScene”, scene3 )
scene3:addEventListener( “destroyScene”, scene3 )
return scene3
– END SAMPLE CODE
When I run this app and click on each one of the images here I what I get in the simulator output window:
Initial Memory Values: main.lua
Memory Usage: 84.6044921875
Texture memory: 0
Scene1 Memory Values: Light Switch Image
Memory Usage: 108.6572265625
Texture memory: 0.008192
Scene2 Memory Values: Bird Image
Memory Usage: 112.470703125
Texture memory: 0.540672
Scene3 Memory Values: Trading Card Image
Memory Usage: 116.2490234375
Texture memory: 9.445376
Scene1 Memory Values: Light Switch Image
Memory Usage: 113.72265625
Texture memory: 9.445376
The first four outputs are as expected. Each time the scene changes and a new image is displayed the memory usage and texture memory increase. What I don’t understand is the last output block when I return to scene 1. In the exitScene function of scene 3 I have a storyboard.removeAll() function call. My understanding is that this would call the destroyScene method and remove everything from memory and free all the available memory. Thus my output readings for Scene 1 should be the same both times. The second output for scene 1 looks like no texture memory was freed up at all. What is the storyboard.removeAll() or the storyboard.purgeAll() functions doing? How do I free up memory when I leave a scene?
Thanks,
Jason Presley