question about removing Scene

hello,

I have 2 scenes,  and I find something strange :

I put a button widget on the first “scene1” like this :

local widget=require("widget") local composer = require( "composer" ) local scene = composer.newScene() -- --"scene1.lua" -- function scene:create( event )     local view = self.view     local btn=widget.newButton({x=200,y=200,width=100,height=100,shape="rect",   onRelease=function() composer.removeScene( "scene1" ) timer.performWithDelay( 3000, function() composer.gotoScene("scene2") end)   end})   view:insert(btn) end function scene:show( event ) end function scene:hide( event ) end function scene:destroy( event ) end scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) return scene

When I released the button, there are a runtime error :

ERROR: Runtime error ?:0: attempt to index field 'contentBounds' (a nil value) stack traceback: ?: in function '\_saveSceneAndHide' ?: in function 'gotoScene' C:\Users\Yvan\Documents\Corona Projects\test\scene2.lua:34: in function '\_listener' ?: in function '?' ?: in function \<?:190\>

But when the composer remove the scene 1 INSIDE the timer,

onRelease=function() timer.performWithDelay( 3000, function() composer.removeScene( "scene1" ) composer.gotoScene("scene2") end) end

there are no error. 

Why ?? Thanks.

You can’t remove “scene1” while you’re still in it.

Are you sure ?

When it’s inside the timer it’s work.

And another question : how to reload “refresh” the same scene ?

I think you’re probably getting lucky with the timer that gotoScene runs before removeScene has had a chance to complete.

As for refreshing, I sometimes have a blank ‘loading.lua’ scene thats only job is to remove the scene you came from, and after a short delay (say 50ms) call gotoScene to go back to the original scene.

Alternatively you can just create a function that removes all the display objects manually, resets any variables to their default status and then calls code to re-draw the scene.

Yes I think it’s luck because in fact without timer it’s works : 

composer.removeScene( "scene2" ) composer.gotoScene("scene1")

The gotoScene comes so quick that the removeScene function have no time to remove the hide listener of the scene2, but I am not sure.

@yvandotet,

Nick is right.  You can’t remove a scene from within the same scene.

If you need to remove the contents of a scene while still in it, modularize your scene’s game code and create, destroy, etc. functions.

I would suggest that if you want to make a scene that is created and destroyed on each entry/exit, then don’t use the create/destroy methods.

Use the show() (will phase) to create.

Use the hide (did phase) to destroy.

Keep all of your content in a group where that group is added to the view.  Then, destroying the contents is as easy as removing your local temporary group.

So basically you can’t remove the scene you’re in because there wouldn’t be anything to show. You can remove the scene once another scene is on screen.

This is why the timer works. Your next scene is showing. You can also put the removeScene() call in the scene:hide() function during the “did” phase since the scene is hidden at that point. You can’t during the “will” phase since the scene is still on screen.

This trips up a lot of people. You have other options too. Set the scenes to auto remove by setting:

composer.recycleOnSceneChange = true

https://docs.coronalabs.com/api/library/composer/recycleOnSceneChange.html

This only dumps the view group, the scene’s module is still required. This means when you go back to the scene, scene:create() will be called again, but any objects that exist outside of there or variables that are set, won’t be reset.

The big question is why are you removing the scene?

  1. to save memory?

  2. to make it easy to go back to the scene and have everything in place?

  3. Some other reason?

Composer caches scenes to be more efficient when returning to them. Unless you’re hitting memory limits, you shouldn’t be removing scenes in general. However most of the time, people want to do this because it makes the scene start fresh. Your scene is a complete Lua module. That means you can define local variables at the top that can hold values like scores, lives, spawned enemies.  Removing the scene is an easy, but cruel way to do this. Now Composer has to reconstruct the scene, reading all the image files, audio files, etc. in and doing all that work again, when a few lines of code  could reposition objects, purge spawn tables and clear the high score.

Now personally being lazy, I’ll make Composer do the work again to save me coding time. Best practice? Probably not. Since memory is not a concern, I will stack my removeScene/gotoScene like this:

composer.removeScene(“theSceneImGoingTo”)

composer.gotoScene(“theSceneImGoingTo”)

I don’t have to deal with any timing issues and it’s simple. If memory is a concern, then I might do something more like:

-- scene I just went to function scene:show( event ) &nbsp; &nbsp; &nbsp;local phase = event.phase &nbsp; &nbsp; &nbsp;if "did" == phase then &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; local previous = composer.getSceneName("previous") &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; composer.removeScene(previous) &nbsp; &nbsp; end end

But as I mentioned above, we’ve recently discovered and tested, what probably should be the best practice with this:

-- scene you are leaving function scene:hide( event ) &nbsp; &nbsp; local phase = event.phase &nbsp; &nbsp; if "did" == phase then &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;local current =&nbsp;composer.getSceneName("current") &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; composer.removeScene(current) &nbsp; &nbsp; end end

I would recommend going with this method even though it not what I do :slight_smile: I can be a bad programmer at times!

Rob

Ok thanks for all.

I asked that questions in studying this github : https://github.com/englekk/WonHadaHSliderSample/blob/master/Project/MainSceneStarter.lua

It’s from “englekk”, a corona member that looks very skill, and he does the same thing that you have written before.

It’s more a theorical question than practical.

I will use 

local previous = composer.getSceneName("previous") composer.removeScene(previous)

in the scene2 " will " show portion.

I think it’s the best.

You can’t remove “scene1” while you’re still in it.

Are you sure ?

When it’s inside the timer it’s work.

And another question : how to reload “refresh” the same scene ?

I think you’re probably getting lucky with the timer that gotoScene runs before removeScene has had a chance to complete.

As for refreshing, I sometimes have a blank ‘loading.lua’ scene thats only job is to remove the scene you came from, and after a short delay (say 50ms) call gotoScene to go back to the original scene.

Alternatively you can just create a function that removes all the display objects manually, resets any variables to their default status and then calls code to re-draw the scene.

Yes I think it’s luck because in fact without timer it’s works : 

composer.removeScene( "scene2" ) composer.gotoScene("scene1")

The gotoScene comes so quick that the removeScene function have no time to remove the hide listener of the scene2, but I am not sure.

@yvandotet,

Nick is right.  You can’t remove a scene from within the same scene.

If you need to remove the contents of a scene while still in it, modularize your scene’s game code and create, destroy, etc. functions.

I would suggest that if you want to make a scene that is created and destroyed on each entry/exit, then don’t use the create/destroy methods.

Use the show() (will phase) to create.

Use the hide (did phase) to destroy.

Keep all of your content in a group where that group is added to the view.  Then, destroying the contents is as easy as removing your local temporary group.

So basically you can’t remove the scene you’re in because there wouldn’t be anything to show. You can remove the scene once another scene is on screen.

This is why the timer works. Your next scene is showing. You can also put the removeScene() call in the scene:hide() function during the “did” phase since the scene is hidden at that point. You can’t during the “will” phase since the scene is still on screen.

This trips up a lot of people. You have other options too. Set the scenes to auto remove by setting:

composer.recycleOnSceneChange = true

https://docs.coronalabs.com/api/library/composer/recycleOnSceneChange.html

This only dumps the view group, the scene’s module is still required. This means when you go back to the scene, scene:create() will be called again, but any objects that exist outside of there or variables that are set, won’t be reset.

The big question is why are you removing the scene?

  1. to save memory?

  2. to make it easy to go back to the scene and have everything in place?

  3. Some other reason?

Composer caches scenes to be more efficient when returning to them. Unless you’re hitting memory limits, you shouldn’t be removing scenes in general. However most of the time, people want to do this because it makes the scene start fresh. Your scene is a complete Lua module. That means you can define local variables at the top that can hold values like scores, lives, spawned enemies.  Removing the scene is an easy, but cruel way to do this. Now Composer has to reconstruct the scene, reading all the image files, audio files, etc. in and doing all that work again, when a few lines of code  could reposition objects, purge spawn tables and clear the high score.

Now personally being lazy, I’ll make Composer do the work again to save me coding time. Best practice? Probably not. Since memory is not a concern, I will stack my removeScene/gotoScene like this:

composer.removeScene(“theSceneImGoingTo”)

composer.gotoScene(“theSceneImGoingTo”)

I don’t have to deal with any timing issues and it’s simple. If memory is a concern, then I might do something more like:

-- scene I just went to function scene:show( event ) &nbsp; &nbsp; &nbsp;local phase = event.phase &nbsp; &nbsp; &nbsp;if "did" == phase then &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; local previous = composer.getSceneName("previous") &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; composer.removeScene(previous) &nbsp; &nbsp; end end

But as I mentioned above, we’ve recently discovered and tested, what probably should be the best practice with this:

-- scene you are leaving function scene:hide( event ) &nbsp; &nbsp; local phase = event.phase &nbsp; &nbsp; if "did" == phase then &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;local current =&nbsp;composer.getSceneName("current") &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; composer.removeScene(current) &nbsp; &nbsp; end end

I would recommend going with this method even though it not what I do :slight_smile: I can be a bad programmer at times!

Rob

Ok thanks for all.

I asked that questions in studying this github : https://github.com/englekk/WonHadaHSliderSample/blob/master/Project/MainSceneStarter.lua

It’s from “englekk”, a corona member that looks very skill, and he does the same thing that you have written before.

It’s more a theorical question than practical.

I will use 

local previous = composer.getSceneName("previous") composer.removeScene(previous)

in the scene2 " will " show portion.

I think it’s the best.