General question on Corona game design patterns (object references storage, etc)

I’m learning Corona but I’ve been developing software in other languages for over a decade (not games, though). I’m in the process of creating a Tower Defense game just for fun in order to learn the SDK. 

My question has to do with how to organize object creation/destruction inside the game’s class structure.

For example, the root object is the “level” (as in level1.lua) and that contains a “scene” object. So far this is kind of dictated by Corona if you want to use scenes (I do). If you open the game template project, this is what you get so I’m assuming it’s the suggested best practice for this kind of app.

Question #1

Who should be listening to the enterFrame event? level1 or scene?

Question #2

The blank scene:create() template has a screneGroup variable where they state: “all display objects must be inserted into group”. That’s fine for the hard coded display objects that you instanciate during scene creation but in my case, a ton more objects will be created and destroyed on the fly as the user interacts with the game.

What is the function of this sceneGroup variable?

On a related note the sceneGoup variable is defined as local in the scene:create() function so as soon at that block executes, the reference to it is lost forever. If I need to add more objects to this group after the function has finished executing (which I’m sure happens in 99% of Corona games), why would they have it be a local function?

Question #3

Where should you keep references to your dynamically created display objects?  Let’s say I tap a tower and create a popover tower upgrade menu. Where should I keep a reference to it to be able to destroy it after the user taps somewhere else? And where do you store the rest of your display object (towers, creeps, maps, UI, etc).

Note that I’m not particularly looking for actual code (I think I can figure that our by myself reading the docs) but I’m looking for conceptual answers.

Thanks in advance. 

Let me take a stab at these. 

  1. enterFrame listeners are attached to the Runtime.  They don’t specifically belong to any module.  Because of the nature of the scene:show and scene:hide functions (they always execute in pairs) and you don’t want Runtime events triggering while your scene’s are off screen or in the process of transitioning on or off screen.  You should do any Runtime:addEventListener() calls in scene:show() (did phase) and remove them in scene:hide() (will phase).

  2. sceneGroup is a localized convenience reference to “self.view”.  Self is of course a reference to the scene object itself.  So for any scene function that has a : in it (i.e. scene:show), the variable “self” is passed in for you which is the scene itself.  Ergo:

scene.view = self.view (in a scene method) = local sceneGroup (reference in the scene). 

If you have a function that isn’t a method attached to the scene you can do the same thing like this:

local function spawnEnemy()

     local sceneGroup = scene.view

end

3.  I normally track my objects in a table:

local creeps = {}

Rob

Rob, thanks for the prompt reply. It’s starting to become clearer now. However, I do have some follow up questions if you don’t mind. 

Regarding “enterFrame” events. I understand that listeners are attached to the Runtime but what I was asking was if there was a convention on who was supposed to handle the actual calls. For example which of the following should I be doing?

local localObject = require("foo") function localFunction(event) -- do stuff end function scene:create( event ) -- do stuff end Runtime:addEventListener("enterFrame", localFunction); Runtime:addEventListener("enterFrame", localObject); Runtime:addEventListener("enterFrame", scene);

You should do any Runtime:addEventListener() calls in scene:show() (did phase) and remove them in scene:hide() (will phase).

That’s great to know!

Regarding object tracking. Do you have to call… 

self.view:insert(obj)

…for every object you create?

If so, let’s say you are four objects deep into the hierarchy and you need to create another display object. Do all your objects usually contain a reference to self.view?

I do the first form, a local function.

If you want storyboard to manage the objects, then you have to insert the objects into the scene’s view.  If you plan on managing them yourself then you don’t need to put them into the scene’s view.  The trade off is that if you change scenes, anything you don’t put in the view won’t transition off the screen and won’t be cleaned up when the scene is removed.

Rob

Oh, I see. Thanks for all the info!

As someone with a tendency to engineer things a bit much, I’ve written a thing called ‘Executive’ which is still under development https://github.com/autismuk/Executive

Basically an executive object is an object container with timer, update and messaging facilities built in. Object management is semi automatic - there is a proper destructor for example and things tidy up after themselves - and objects can be tagged and the group queried.

So in the flappy bird demo, you create an executive object and add bird, pipe, background, score objects to it and they talk between each other.

It also has a bit more alpha extension where you can build an FSM with transitions and executive factory classes which basically replaces Composer/Storyboard, and the tighter object system means it does most of the work for you.

It came from my experiments with classic OOP (too rigid) and Entity/Component systems too fiddly. It’s an attempt at a compromise.

Hi.  I just posted about this for another forum question, but you may find my recent articles useful/interesting:

http://roaminggamer.com/2014/06/26/simple-bench-for-corona-sdk/
http://roaminggamer.com/2014/06/26/tracking-objects-in-tables-best-practices-corona-sdk/

romainggamer, those are very interesting reads. Thanks for sharing!

By the way, roaminggamer, when you mention ‘pairs()’ Iteration in your article, the code example is exactly the same as integer iteration. Is it a copy/paste typo?

@Chillfok -Thanks for catching the paste error.  Just fixed it.

Let me take a stab at these. 

  1. enterFrame listeners are attached to the Runtime.  They don’t specifically belong to any module.  Because of the nature of the scene:show and scene:hide functions (they always execute in pairs) and you don’t want Runtime events triggering while your scene’s are off screen or in the process of transitioning on or off screen.  You should do any Runtime:addEventListener() calls in scene:show() (did phase) and remove them in scene:hide() (will phase).

  2. sceneGroup is a localized convenience reference to “self.view”.  Self is of course a reference to the scene object itself.  So for any scene function that has a : in it (i.e. scene:show), the variable “self” is passed in for you which is the scene itself.  Ergo:

scene.view = self.view (in a scene method) = local sceneGroup (reference in the scene). 

If you have a function that isn’t a method attached to the scene you can do the same thing like this:

local function spawnEnemy()

     local sceneGroup = scene.view

end

3.  I normally track my objects in a table:

local creeps = {}

Rob

Rob, thanks for the prompt reply. It’s starting to become clearer now. However, I do have some follow up questions if you don’t mind. 

Regarding “enterFrame” events. I understand that listeners are attached to the Runtime but what I was asking was if there was a convention on who was supposed to handle the actual calls. For example which of the following should I be doing?

local localObject = require("foo") function localFunction(event) -- do stuff end function scene:create( event ) -- do stuff end Runtime:addEventListener("enterFrame", localFunction); Runtime:addEventListener("enterFrame", localObject); Runtime:addEventListener("enterFrame", scene);

You should do any Runtime:addEventListener() calls in scene:show() (did phase) and remove them in scene:hide() (will phase).

That’s great to know!

Regarding object tracking. Do you have to call… 

self.view:insert(obj)

…for every object you create?

If so, let’s say you are four objects deep into the hierarchy and you need to create another display object. Do all your objects usually contain a reference to self.view?

I do the first form, a local function.

If you want storyboard to manage the objects, then you have to insert the objects into the scene’s view.  If you plan on managing them yourself then you don’t need to put them into the scene’s view.  The trade off is that if you change scenes, anything you don’t put in the view won’t transition off the screen and won’t be cleaned up when the scene is removed.

Rob

Oh, I see. Thanks for all the info!

As someone with a tendency to engineer things a bit much, I’ve written a thing called ‘Executive’ which is still under development https://github.com/autismuk/Executive

Basically an executive object is an object container with timer, update and messaging facilities built in. Object management is semi automatic - there is a proper destructor for example and things tidy up after themselves - and objects can be tagged and the group queried.

So in the flappy bird demo, you create an executive object and add bird, pipe, background, score objects to it and they talk between each other.

It also has a bit more alpha extension where you can build an FSM with transitions and executive factory classes which basically replaces Composer/Storyboard, and the tighter object system means it does most of the work for you.

It came from my experiments with classic OOP (too rigid) and Entity/Component systems too fiddly. It’s an attempt at a compromise.

Hi.  I just posted about this for another forum question, but you may find my recent articles useful/interesting:

http://roaminggamer.com/2014/06/26/simple-bench-for-corona-sdk/
http://roaminggamer.com/2014/06/26/tracking-objects-in-tables-best-practices-corona-sdk/

romainggamer, those are very interesting reads. Thanks for sharing!

By the way, roaminggamer, when you mention ‘pairs()’ Iteration in your article, the code example is exactly the same as integer iteration. Is it a copy/paste typo?

@Chillfok -Thanks for catching the paste error.  Just fixed it.