clearing display.currentStage - 2 questions

Do not call sceneRemove() in hide() unless you do it in the ‘did’ phase and delay it by at least 1ms:

function scene:hide( event ) if( event.phase == "did" ) then timer.performWithDelay( 1, function() \<DO THE CALL HERE\> end ) end end

It is a bad idea to ‘destroy’ the current scene while executing a script in the current scene.

Therefore a slight delay (as above) causes the removal to be done in the next frame (after the function/listener call completes).

First of all, thanks for the quick response!

If I could pick you brain a little more, I now have 3 questions about the first two questions

On my first question (Could my mistake of removing an object before removeScene() could remove it, have caused  removeScene() to _not_ remove all objects?) you mentioned:

“make one or more groups and insert your content into them. Then, later when you need to destroy all that content, just destroy the group(s).”

I do insert all my display objects into groups, but normally I don’t explicitly destroy them. Later when I need to destroy that content, I call removeScene() intending that removeScene() will destroy all the groups and the display objects within them

1a) That is good practice, correct?

1b) If, because of my mistake, one of the objects _inside_ one of the groups had already been deleted, perhaps that would cause the problem I observed, intermittently?

So, if the wisdom to be learned here is “don’t ever delete an object that is in a display group. Let removeScene() delete the group and it will delete all the objects within it. I’d be happy with that. I thought that I read that after removing a display object I also had to set the value of a variable pointing to that object to nil, but I’d be glad to _not_ do that.  

On the second question (How can I clear out the display stage…) I don’t insist on doing it child by child, I’m just looking for a way to guarantee that all the display objects from prior scene(s) are removed. You mentioned

-- Better way to handle deletions --... --... later destroy all display.remove(group)
  1. OK. I thought I was essentially doing that when I called removeScene(), but perhaps I need to explicitly call display.remove() for each of the groups I created?

As an aside, I did try removing all the 3 mystery children in reverse order. The mystery children being the ones that that are there before I execute any other code in scene:create()  

I used the for loop style code like this…

 for i = stage.numChildren, 1, -1 do local child = stage[i] display.remove(child) print( "stageChild["..i.."] was removed " ) end

I got these log messages

11:58:36.365  stageChild[3] was removed

11:58:36.365  stageChild[2] was removed

11:58:36.365  stageChild[1] was removed

But after that, the rest of my code in scene:create() and scene:show()that normally produces a screen just produced a black screen.

OK, after clicking submit, I see you posted some more.

Let me go back and sort it out

Dan,
 
This will probably be my last response.  You’ve got way too many questions in the original post, so I’ll just re-review your original post and provide terse answers.
 

Occasionally, some display objects from the last game aren’t cleared from the display.

If this happens, the object(s) were not inserted into scene group OR were not in a group that you later destroyed.
 
Go back and check your code for places where you did not insert a object into a group and/or did not destroy that group.
 

 

inserting all my display objects into groups and then inserting them into the sceneGroup.

 
Yes.  Do this.
 

At the end of every game/scene I call composer.removeScene() on scene:hide() intending to remove everything before the next scene. 

Do not do this as stated in your question.  Instead use the method I showed above where it is done in the ‘did’ phase of hide() and then only inside a call to timer.peformWithDelay() so the remove scene action occurs AFTER the scene  listener executes.

I had to continue this in a second post because to ‘quote’ all your questions put me beyond the allowed ‘quote’ limit for a post.

I did discover an error where I had set one of my display objects to nil, without first removing it from the display. My first question is…

This isn’t really clear.
 
If you’re doing this, then you’ve got as big problem:

local obj = display.new\*( ... ) obj = nil ... then later display.remove(obj) -- DOES NOTHING

However, if you’re clearing deleting the object by destroying the group it is in or by iterating over the group and destroying objects one by one, then you’re fine.
 

 

Maybe I can explicitly remove all objects from the main stage 

Don’t do that.  The main stage is not a scenegroup.  Leave it alone.
 
 
 

  1. How can I clear out the display stage before I create a new scene?

Again, do not clear the (current) stage. Simply destroy any groups you created in the scene.
 
This is how I make my composer scenes to avoid ALL possible confusion about phases:

-- ============================================================= -- Your Copyright Statement Here, YYYY-YYYY -- ============================================================= -- Scene Description Here -- ============================================================= local composer = require( "composer" ) local scene = composer.newScene() ---------------------------------------------------------------------- -- LOCALS ---------------------------------------------------------------------- local content ---------------------------------------------------------------------- -- FORWARD DECLARATIONS ---------------------------------------------------------------------- -- None ---------------------------------------------------------------------- -- Scene Methods ---------------------------------------------------------------------- ---------------------------------------------------------------------- ---------------------------------------------------------------------- function scene:create( event ) local sceneGroup = self.view end ---------------------------------------------------------------------- ---------------------------------------------------------------------- function scene:willShow( event ) local sceneGroup = self.view -- content = display.newGroup() --[[NOW PUT CONTENT YOU CREATE IN 'content' or a child of 'content' --]] end ---------------------------------------------------------------------- ---------------------------------------------------------------------- function scene:didShow( event ) local sceneGroup = self.view end ---------------------------------------------------------------------- ---------------------------------------------------------------------- function scene:willHide( event ) local sceneGroup = self.view end ---------------------------------------------------------------------- ---------------------------------------------------------------------- function scene:didHide( event ) local sceneGroup = self.view -- -- Destroy all scene content in one fell-swoop. -- display.remove( content ) content = nil end ---------------------------------------------------------------------- ---------------------------------------------------------------------- function scene:destroy( event ) local sceneGroup = self.view end ---------------------------------------------------------------------- -- FUNCTION/CALLBACK DEFINITIONS ---------------------------------------------------------------------- --------------------------------------------------------------------------------- -- Scene Dispatch Events, Etc. - Generally Do Not Touch Below This Line --------------------------------------------------------------------------------- function scene:show( event ) local sceneGroup = self.view local willDid = event.phase if( willDid == "will" ) then self:willShow( event ) elseif( willDid == "did" ) then self:didShow( event ) end end function scene:hide( event ) local sceneGroup = self.view local willDid = event.phase if( willDid == "will" ) then self:willHide( event ) elseif( willDid == "did" ) then self:didHide( event ) end end scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) --------------------------------------------------------------------------------- return scene

OK in sorting out the previous posts, I think I’m basically doing creating/deleting the way you recommend, except for the point about …

Do not call sceneRemove() in hide() unless you do it in the ‘did’ phase and delay it by at least 1ms:

I do call it in the “did” phase but I’m not using any delay now, and it seems like not doing that could causer a race / intermittent problem like I see.

So, I think my bottom line is:

  • Add the delay
  • Forget about the 3 mystery children. I don’t need any more mysteries right now.

Oh, and maybe one other bottom line task for me. I’m pretty sure I found that technique of calling removeScene() in hide() in one of the tutorials. I’ll go back and hunt for that (for a little while at least). If I do find it, I’ll use that documentation flag to point out that a delay is required.

Thanks!

Found it! The tutorial with scene:hide() calling composer.removeScene() is here: 

https://docs.coronalabs.com/guide/programming/05/index.html 

That tutorial says:  

With this simplified approach, let’s modify our scene:hide() function:

-- hide() function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- Code here runs when the scene is on screen (but is about to go off screen) timer.cancel( gameLoopTimer ) elseif ( phase == "did" ) then -- Code here runs immediately after the scene goes entirely off screen Runtime:removeEventListener( "collision", onCollision ) physics.pause() composer.removeScene( "game" ) end end

This addition should be clear — we simply call composer.removeScene( “game” ) within the “did” phase of scene:hide(), effectively destroying the scene after it transitions fully off screen.

I flagged it.

Gotta go wrap Christmas presents!

Happy holiday to all.

Making sure all (display) objects are properly removed is a big part of debugging and betatesting for me. All my displayGroups get a .name property that allows me to do some quick object-hunting. After playing a couple of levels I go to a special debugmodule that goes over all displayObjects on the currentStage. If there is a .name propery I output the name so I know who the culprit is. It there is no .name property I know it’s not a group but instead ‘visible’ displayObject - I then set alpha to 1 and center it on the screen.

So either I see the objects visually, or I see the name of the group. That gives me an idea of where to look for bad cleanup routines.

That being said, my code structuring has becoming so much of an automatic routing that I rarely make errors of this kind.

Also, my whole game always runs in a top-level displayGroup. I can effectively run multiple copies of my game side by side if I want to, on a single device. Doing everything in a proprietary top-level displayGroups allows you to do quick-and-dirty object removal as well - although that’s more of a fix than good coding habits.

On a final note: I don’t use composer. Don’t see the appeal for it either, but that’s just my personal opinion.

@dan56 filled out a documentation request on this, and I thought I should share my response here:

Generally speaking, you should not remove the scene that’s currently on the screen. After some testing, we found it’s safe to remove the current scene in the scene:hide()'s “did” phase. This is what the Getting Started guide says. The Getting Started guide is meant to not overwhelm the new developer with more obscure use cases. It’s likely safer to drop the composer.removeScene() inside a short timer to make sure all other things like pausing physics, timers being removed etc. actually happen before you remove the scene. In the case of the game you’re building, it should be safe to do it without a timer, but other users may find a timer to be more practical:
 

function scene:hide( event ) &nbsp;&nbsp;&nbsp; if ( event.phase == "did" ) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timer.performWithDelay( 100, function() composer.removeScene( "game" ); end ) &nbsp;&nbsp;&nbsp; end end

assuming of course that your scene is named “game”. A 1-second timer will execute in the next frame which is either 1/30th of a second or 1/60th of a second later, not really in 1 millisecond. If your frame rate is 60 fps, the next frame will happen in 17 milliseconds, so any
time between 1 and 17 will be the same frame. It’s also not going to hurt to wait even longer. Your user isn’t going to react in a 10th of a
second to go back to your scene.

Now for some additional commentary…

Now, all that said, I am not a fan of removing scenes even in scene:hide() anyway. There is no harm leaving a scene in memory, unless you need to free the memory. It’s more work, but the **right way** to reset a scene and be more computer efficient is to reset objects during scene:show()'s “will” phase. Move your objects back to their starting position, reset rotations, set physics starting values, etc. If you want to take advantage of scene caching, this is what you should do. Yes, it’s more work on you. It takes planning and effort to get ti right.

Programmers are always looking for efficiency. That, of course, could be more efficient ways to do things like faster sorting methods, but frequently it’s ways to write less code. Removing a scene is a brute force way of resetting a scene, but it’s just that a brute force method. Yes, you write one line of code vs. potentially dozens or hundreds, but you’re causing the system to have to recreate things, reload images from storage, etc. While one developer might call it programmer efficiency, others could call it a lazy hack.

It’s one I’m quite guilty of in my own apps and games. I will remove the scene to reset it. However, I almost never reset it in the scene:hide() “did” phase. Instead, I remove the scene before I go to it. It’s pretty easy to do:

composer.removeScene("game") composer.gotoScene("game")

You’re not messing with timers. You’re not messing if the scene is still on the screen or not. You’re not messing with deleting yourself.

And if you’re thinking about saving lines of code, it doesn’t really save that much. If you’re already doing:

local player = display.newImageRect(“player.png”, 100, 1000)

sceneGroup:insert(player)

player.x = 50

player.y = display.contentCenterY

physics.addBody( player, …)

player:addEventListener( “touch”, playerTouchHandler )

in scene:create(), it’s not that much more work to:

local player -- at the top of the module outside of any functions for scope

in scene:create()

player = display.newImageRect("player.png", 100, 1000) sceneGroup:insert(player)

in scene:show()

if event.phase == "will" then &nbsp; &nbsp; player.x = 50 &nbsp; &nbsp; player.y = display.contentCenterY &nbsp; &nbsp; physics.addBody( player, ...) &nbsp; &nbsp; player:addEventListener( "touch", playerTouchHandler )end

That’s one more line of code. But you would want to remove the physics body in scene:hide()'s “did” phase. You may need to set the initial rotation, and physics properties too, which is where the extra code comes into play.

 
Rob