Structuring An Adventure Game With Storyboards

I’m writing an adventure game with a lot of static scenes. I’m having a lot of trouble structuring my code efficiently. The way I see it I have two choices. Either:

 

  1. Make every scene a lua file. this is what I chose. I can leverage the nice transitions and memory management

  2. Create a single “game”-scene which swaps out which scene it is displaying

 

Both have problems I keep encountering.

 

  1. Makes common stuff a chore to maintain. I’ve been trying to place common stuff in a common.lua and requiring it from every scene. I also try to make external modules with logic and require them from the scenes and/or the common.lua. But things keep disappearing or end up in incomplete states (for example an image or rect with alpha 1 that is not visible on one scene but is on another).

  2. Is a lot more difficult to code and the entire storyboard API is rendered more or less unnecessary. I chose not to go this way.

 

It feels like there is a better way… I’m getting pretty sick of managing images and listeners etc in every scene and things keep messing up, I’m assuming from things getting unloaded from memory at unpleasant times… it’s really difficult to debug these things.

 

Any ideas/experience to share would be greatly appreciated.

Hey Benny5,

I’ve been working on an app that requires a lot of common code as well. Each of my scenes is a lua file, and I’ve ended up making function libraries to deal with things. Just to check, are you putting the code inside functions in common.lua, or just in the body itself? The way that I understand required to work is that is executes all code outside of functions the first time it is called and doesn’t on repeat calls, since its already loaded. If this is the problem then you have two solutions.

  1.  Unload common.lua and reload it.
  2.  Put all your common code in functions and call them (this is the path I took): eg

[lua]local sceneSetup = {}

local widget = require “widget”

local storyboard = require (“storyboard”)

local function goHomeListener(event) 

    print(event.name…" occurred for goHome of type "…event.phase) 

    storyboard.gotoScene( “scene.5_3_home”, { effect = “crossFade” , time = 500} )

end 

            

print (“scene-setup-library has been loaded”)

sceneSetup.createBackground = function(self, otherView, isBack, hasHome)

    … various code that sets up objects and adds them to a display group …

    return displayGroup

end

[/lua]

Provided all the variables are local, they don’t clash with each other, I can then invoke the common code using:

[lua]

local sceneSetup = require(“sceneSetupLibrary”)

local group = sceneSetup.createBackground(self, topBarView, false, false);

self.view:insert(group)

[/lua]

If thats what your already doing, then I’m sorry that I couldn’t be of help. I’ve only been using Corona for about a week, so this could all be horribly inefficient and wrong of course.

Hey Simon,

I’m just glad to hear from anyone at this point :slight_smile: You have an impressive understanding of Corona for just a weeks’ use I must say. I’ve been using it for a few weeks only myself. I do, as I suspect you do as well, have quite a few years of expeirence in c++, java, php, javascript and a few other languages so lua is pretty quick to pick up. The internal structures are a bit confusing as this is pretty high level though.

You are correct and that is what I’m doing actually. But it becomes a mess when some things should be unloaded and some not from these external libraries. And sometimes things disappear out of memory after a while because they’re not properly maintained within the storyboard create,enter,exit,destroy functions. I’ve managed to clean this up a bit better now but it doesn’t seem that good to call for example in your case createbackground for every scene. Shouldn’t there be a better way to do it once and then keep it for several scenes until it isn’t needed anymore rather than creating every scene from scratch with basically the same assets?

As you probably know you have to keep damned good track of every timer,listener,transition,image and audiofile/channel or you’ll end up breaking the game in weird ways or leaking lots of memory. I’m looking for a way to centralise this efficiently basically. Would it for example be a good idea to have functions to add timers etc that itself will keep track of them and release them in a release-function I can put in exitScene/destroyScene? I’ve been playing with that thought but not decided to recode everything this way yet.

Hey Benny,

That createBackgroup method is a little misnamed, it actually sets up a branded topbar as well as the background, a background image and a couple of buttons that sit on it with attached listeners that change scenes. It is a little frustrating that it has to be set up each time, but it makes sense to me. I see storyboard as just that, like a picture book. Nothing is assumed to be the same between them. What I think I should be doing, though I don’t know if it can be done, is creating my header and static background, and then dynamically changing a nested displaygroup with storyboard, though I don’t think that is how it works. I’ll have to try it out when I have a bit more knowledge.

As for my understanding, I’m developing a business application at the moment, so I’ve not had to worry to much about a lot of things the engine could do. I knew that I wanted to use storyboard and certain buttons / widgets, so I scanned the backlog of the blog and just read every article that seemed relevant to what I wanted to do. Mostly Widgets 2.0 and Storyboard for me, native text boxes are proving to be a complete pain, though I have a solution for them at the moment that I plan to put up in the common code section when I get around to cleaning it up.

I’ve not gotten around to doing scene cleanup at the moment, trying to get all the graphics in place so that I can get a graphic artist to work on the assets while I code the functionality. As far as keeping track of all the timers, listeners and others goes, I’m using three methods.

Common code: These are just sitting around and being reused. If I have specific code that gets used a lot, I’m using a module to store it, and nothing prevents you from assigning telling an object to attach to a listener in a loaded module. The listener sits around, but I believe that when the object disappears, it becomes unregistered (to be confirmed). Same goes with functions I’ll be using a lot.

Local listeners: Reading up about local variables for lua, I believe that they clean themselves up when the function exits. Since a scene is itself just a special object created when the scene is moved to, removing the object should remove local variables, I hope. I believe the scene:remove and scene.purge are what I’m looking at using.

Other things: Not so common code. I don’t really have enough to this to worry about, but I suspect that you might do. I recommend that if you have code that is going to be called a few times, but not be needed for every scene, you load a module, and then unload it at the end of the scene, provided its not carrying useful information. That way it won’t stay open in memory.

Anyway, this is a combination of what I’ve found and some assumptions I’ve made. At the end of the day, don’t you have to manage memory in C anyway, clearing objects up after your done with them?

From what I understand I think you understood it correctly :slight_smile: Each scene is completely it’s own and you’re not really supposed to reuse anything between them and everything should be recreated from scratch.

Regarding common code, if it’s not one of the types I listed I think you can do whatever you want with it. The garbage collector handles it.

When you say “module” you just mean a separate lua file like your sceneSetupLibrary earlier right?

local f = {} f.name = function() -- do stuff end -- ...more functions return f 

Listeners attached to DisplayObjects are released when the displayobject does object:removeSelf() yes, however you usually create the display object in onCreateScene and attach the listener in onEnterScene and from what I’ve read you’re supposed to release listeners on onExitScene so you only actually take advantage of that when onDestroyScene is called. For recurring scenes you may not want to purge in-between scenes since you have to recreate it when you come back. In this case you need to manage all listeners manually in onenterscene and onexitscene. Which makes it a hassle if you create stuff in an external module. You’d need to create a create function, an addListeners function and a removeListeners function to everything in every module.

GroupObjects also release DisplayObjects contained within so adding stuff to self.view actually helps a lot with the cleanup.

But say like in my game I want a timer to run and show time left in every scene. I’d want the timer and it’s current state outside of the scenes and I’d need to add and remove the timer and it’s text object on every onenterscene and onexitscene and only keep the data in an external module. How would I store that data?

Currently I think I’d have to do something like this. I have no idea if its the best way:

An external module countdownTimer.lua required from several scenes:

local ct = {} local time = 15 -- simplified :) -- Q: Will this stay the same value between scenes? Should I place this kind of common data somewhere else? local timer = nil -- Q: is this ever killed by Corona or can I count on it being alive until the app closes? local timerText = nil local function countdown() time = time - 1 if time \< 0 then -- do things. Q: how to get a common onTimeUp function to get called here? end end ct.create = function() -- where does this go in the scene? if in oncreatescene I'll have tons of these lying around in memory.. if onenterscene I'll also have to destroy them on onexitscene.. maybe thats the right way but then it might be too late and it pops in view after the scene transition is done. timer = timer.performWithDelay(1000, countdown) timerText = display.newText("00:15", x,y, "Arial", 15) return timerText -- this is returned to the scene to be added to self.view end ct.destroy = function() -- Q: so this should be in onexitscene? timer.cancel(timer) timerText = nil -- Q: what happens to the reference now lying in self.view? and how does it affect other scenes self.views? end return ct

I think this code highlights most of the questions I have more concretely.

Yes memory management is a big part of C++ but the language itself has a more clear structure on how to handle it there I think. You have your classes and your destructors and you delete your pointers containing things that have been “new’ed” in initialize functions in your release functions. Here I’m not really sure what’s going on within and outside scenes… a lot of things is happening automatically.

It very much depends on if you want the time to be tracked to each scene individually or not, as well as if your purging.

A simple solution might be the following:

(psudoish, since I haven’t tested this code, and I’m not 100% on the syntax)

[lua]

– store_time.lua

time = {}

return time

[/lua]

[lua]

local time_store = require “store_time”

function scene:createScene( event )

  – create timer with default values

end

function scene:enterScene( event )

  if time_store[“level1”] ~= nil

    timer.time = time_store[“level1”]

  end

  timer.start

end

function scene:exitScene( event )

  time_store[“level1”] = timer.time

  – timer.cancel

end

[/lua]

Obviously replace timer.cancel, timer.start and timer.time with the various correct calls. This has the added advantage of putting all that data in a useful place for you to access it later to save it to a file. You could probably set up methods in time_store and put some of your listeners there. maybe timer.cancel would actually be time_store.cancel(timer) or something. I’ll give some more thought to it tomorrow or this evening and try a more syntactically correct answer then. Hope this helps?

In my case I want the time to carry over between scenes and not have to purge them.

It absolutely helps, if nothing else just bouncing ideas around would give us both a better understanding of how it works. I think my knowledge of the other languages is pushing me into patterns not very fitting to lua. Your idea is similar to an idea I had before:

Instead of local variables in the countdownTimer.lua file, maybe it’s better with table values on the object itself. I.e:

--&nbsp;countdownTimer.lua local ct = {} ct.time = 15 return ct&nbsp;

This should be alive ever since the first require of the file. Maybe you shouldn’t use local file-wide variables in external modules ever as a best practise. Would be great if we could get a second opinion on these things as well from someone having worked with Corona for longer than us.

Thanks for you help! It would be very helpful if you could answer the other questions in my previous code as well when/if you can.

Just to confirm, you want the same timer to be consistent across all the scenes?

My bad, I though you had wanted one time limit per scene.

Yes it should work seamlessly between scenes… sitting in a corner continuing to count down from whatever value on the previous screen. I think this is a good example of something with common data and visual respresentation between scenes.

Makes sense. In that case, I’ll work on something this evening, possibly lunchtime, to see if it is what your looking for. But unfortunately, work calls.

No rush, anything you can do is very much appreciated! I’m busy myself for a couple of days or I would experiment more too.

I’ve been thinking a little more about this. My current structure will be something like this I think. Some 80+ scenes which require an instance of a new common class which has methods for each of the storyboard events to take care of similar operations for every scene. The common class in turn requires in instances of ui classes that create and maintain all that you see. These classes require in common data from lua files that can’t have instances so data is consistent. This means each scene will have a duplicate of everything that is common but I guess that is how it is meant to be. Any thoughts on this approach?

Hey Benny5,

I’ve been working on an app that requires a lot of common code as well. Each of my scenes is a lua file, and I’ve ended up making function libraries to deal with things. Just to check, are you putting the code inside functions in common.lua, or just in the body itself? The way that I understand required to work is that is executes all code outside of functions the first time it is called and doesn’t on repeat calls, since its already loaded. If this is the problem then you have two solutions.

  1.  Unload common.lua and reload it.
  2.  Put all your common code in functions and call them (this is the path I took): eg

[lua]local sceneSetup = {}

local widget = require “widget”

local storyboard = require (“storyboard”)

local function goHomeListener(event) 

    print(event.name…" occurred for goHome of type "…event.phase) 

    storyboard.gotoScene( “scene.5_3_home”, { effect = “crossFade” , time = 500} )

end 

            

print (“scene-setup-library has been loaded”)

sceneSetup.createBackground = function(self, otherView, isBack, hasHome)

    … various code that sets up objects and adds them to a display group …

    return displayGroup

end

[/lua]

Provided all the variables are local, they don’t clash with each other, I can then invoke the common code using:

[lua]

local sceneSetup = require(“sceneSetupLibrary”)

local group = sceneSetup.createBackground(self, topBarView, false, false);

self.view:insert(group)

[/lua]

If thats what your already doing, then I’m sorry that I couldn’t be of help. I’ve only been using Corona for about a week, so this could all be horribly inefficient and wrong of course.

Hey Simon,

I’m just glad to hear from anyone at this point :slight_smile: You have an impressive understanding of Corona for just a weeks’ use I must say. I’ve been using it for a few weeks only myself. I do, as I suspect you do as well, have quite a few years of expeirence in c++, java, php, javascript and a few other languages so lua is pretty quick to pick up. The internal structures are a bit confusing as this is pretty high level though.

You are correct and that is what I’m doing actually. But it becomes a mess when some things should be unloaded and some not from these external libraries. And sometimes things disappear out of memory after a while because they’re not properly maintained within the storyboard create,enter,exit,destroy functions. I’ve managed to clean this up a bit better now but it doesn’t seem that good to call for example in your case createbackground for every scene. Shouldn’t there be a better way to do it once and then keep it for several scenes until it isn’t needed anymore rather than creating every scene from scratch with basically the same assets?

As you probably know you have to keep damned good track of every timer,listener,transition,image and audiofile/channel or you’ll end up breaking the game in weird ways or leaking lots of memory. I’m looking for a way to centralise this efficiently basically. Would it for example be a good idea to have functions to add timers etc that itself will keep track of them and release them in a release-function I can put in exitScene/destroyScene? I’ve been playing with that thought but not decided to recode everything this way yet.

Hey Benny,

That createBackgroup method is a little misnamed, it actually sets up a branded topbar as well as the background, a background image and a couple of buttons that sit on it with attached listeners that change scenes. It is a little frustrating that it has to be set up each time, but it makes sense to me. I see storyboard as just that, like a picture book. Nothing is assumed to be the same between them. What I think I should be doing, though I don’t know if it can be done, is creating my header and static background, and then dynamically changing a nested displaygroup with storyboard, though I don’t think that is how it works. I’ll have to try it out when I have a bit more knowledge.

As for my understanding, I’m developing a business application at the moment, so I’ve not had to worry to much about a lot of things the engine could do. I knew that I wanted to use storyboard and certain buttons / widgets, so I scanned the backlog of the blog and just read every article that seemed relevant to what I wanted to do. Mostly Widgets 2.0 and Storyboard for me, native text boxes are proving to be a complete pain, though I have a solution for them at the moment that I plan to put up in the common code section when I get around to cleaning it up.

I’ve not gotten around to doing scene cleanup at the moment, trying to get all the graphics in place so that I can get a graphic artist to work on the assets while I code the functionality. As far as keeping track of all the timers, listeners and others goes, I’m using three methods.

Common code: These are just sitting around and being reused. If I have specific code that gets used a lot, I’m using a module to store it, and nothing prevents you from assigning telling an object to attach to a listener in a loaded module. The listener sits around, but I believe that when the object disappears, it becomes unregistered (to be confirmed). Same goes with functions I’ll be using a lot.

Local listeners: Reading up about local variables for lua, I believe that they clean themselves up when the function exits. Since a scene is itself just a special object created when the scene is moved to, removing the object should remove local variables, I hope. I believe the scene:remove and scene.purge are what I’m looking at using.

Other things: Not so common code. I don’t really have enough to this to worry about, but I suspect that you might do. I recommend that if you have code that is going to be called a few times, but not be needed for every scene, you load a module, and then unload it at the end of the scene, provided its not carrying useful information. That way it won’t stay open in memory.

Anyway, this is a combination of what I’ve found and some assumptions I’ve made. At the end of the day, don’t you have to manage memory in C anyway, clearing objects up after your done with them?

From what I understand I think you understood it correctly :slight_smile: Each scene is completely it’s own and you’re not really supposed to reuse anything between them and everything should be recreated from scratch.

Regarding common code, if it’s not one of the types I listed I think you can do whatever you want with it. The garbage collector handles it.

When you say “module” you just mean a separate lua file like your sceneSetupLibrary earlier right?

local f = {} f.name = function() -- do stuff end -- ...more functions return f&nbsp;

Listeners attached to DisplayObjects are released when the displayobject does object:removeSelf() yes, however you usually create the display object in onCreateScene and attach the listener in onEnterScene and from what I’ve read you’re supposed to release listeners on onExitScene so you only actually take advantage of that when onDestroyScene is called. For recurring scenes you may not want to purge in-between scenes since you have to recreate it when you come back. In this case you need to manage all listeners manually in onenterscene and onexitscene. Which makes it a hassle if you create stuff in an external module. You’d need to create a create function, an addListeners function and a removeListeners function to everything in every module.

GroupObjects also release DisplayObjects contained within so adding stuff to self.view actually helps a lot with the cleanup.

But say like in my game I want a timer to run and show time left in every scene. I’d want the timer and it’s current state outside of the scenes and I’d need to add and remove the timer and it’s text object on every onenterscene and onexitscene and only keep the data in an external module. How would I store that data?

Currently I think I’d have to do something like this. I have no idea if its the best way:

An external module countdownTimer.lua required from several scenes:

local ct = {} local time = 15 -- simplified :) -- Q: Will this stay the same value between scenes? Should I place this kind of common data somewhere else? local timer = nil -- Q: is this ever killed by Corona or can I count on it being alive until the app closes? local timerText = nil local function countdown() time = time - 1 if time \< 0 then -- do things. Q: how to get a common onTimeUp function to get called here? end end ct.create = function() -- where does this go in the scene? if in oncreatescene I'll have tons of these lying around in memory.. if onenterscene I'll also have to destroy them on onexitscene.. maybe thats the right way but then it might be too late and it pops in view after the scene transition is done. timer = timer.performWithDelay(1000, countdown) timerText = display.newText("00:15", x,y, "Arial", 15) return timerText -- this is returned to the scene to be added to self.view end ct.destroy = function() -- Q: so this should be in onexitscene? timer.cancel(timer) timerText = nil -- Q: what happens to the reference now lying in self.view? and how does it affect other scenes self.views? end return ct

I think this code highlights most of the questions I have more concretely.

Yes memory management is a big part of C++ but the language itself has a more clear structure on how to handle it there I think. You have your classes and your destructors and you delete your pointers containing things that have been “new’ed” in initialize functions in your release functions. Here I’m not really sure what’s going on within and outside scenes… a lot of things is happening automatically.

It very much depends on if you want the time to be tracked to each scene individually or not, as well as if your purging.

A simple solution might be the following:

(psudoish, since I haven’t tested this code, and I’m not 100% on the syntax)

[lua]

– store_time.lua

time = {}

return time

[/lua]

[lua]

local time_store = require “store_time”

function scene:createScene( event )

  – create timer with default values

end

function scene:enterScene( event )

  if time_store[“level1”] ~= nil

    timer.time = time_store[“level1”]

  end

  timer.start

end

function scene:exitScene( event )

  time_store[“level1”] = timer.time

  – timer.cancel

end

[/lua]

Obviously replace timer.cancel, timer.start and timer.time with the various correct calls. This has the added advantage of putting all that data in a useful place for you to access it later to save it to a file. You could probably set up methods in time_store and put some of your listeners there. maybe timer.cancel would actually be time_store.cancel(timer) or something. I’ll give some more thought to it tomorrow or this evening and try a more syntactically correct answer then. Hope this helps?

In my case I want the time to carry over between scenes and not have to purge them.

It absolutely helps, if nothing else just bouncing ideas around would give us both a better understanding of how it works. I think my knowledge of the other languages is pushing me into patterns not very fitting to lua. Your idea is similar to an idea I had before:

Instead of local variables in the countdownTimer.lua file, maybe it’s better with table values on the object itself. I.e:

--&nbsp;countdownTimer.lua local ct = {} ct.time = 15 return ct&nbsp;

This should be alive ever since the first require of the file. Maybe you shouldn’t use local file-wide variables in external modules ever as a best practise. Would be great if we could get a second opinion on these things as well from someone having worked with Corona for longer than us.

Thanks for you help! It would be very helpful if you could answer the other questions in my previous code as well when/if you can.

Just to confirm, you want the same timer to be consistent across all the scenes?

My bad, I though you had wanted one time limit per scene.

Yes it should work seamlessly between scenes… sitting in a corner continuing to count down from whatever value on the previous screen. I think this is a good example of something with common data and visual respresentation between scenes.