Composer bug: Cannot reload scene more than once. (2259)

I don’t believe this is a bug.  It’s just not how you want it to work.  This same issue was present in Storyboard which lead me to do the video tutorial on Reloading Storyboard:

http://coronalabs.com/blog/2013/08/20/tutorial-reloading-storyboard-scenes/

The gist of it is when a scene is on the screen, it cannot be destroyed and re-created… plain and simple.  If it did, there would be nothing to show.

Reloading a scene does not generally require the scene to be recreated.  It simply needs reset.   Keeping in mind any code run (initializing variables, starting physics, etc.) that is in the scene’s main chunk only executes when the scene is “required” for the first time, so:

local livesRemaining = 10

at the top of your module won’t reset unless you’re able to call composer.removeScene() and you shouldn’t try and remove the scene you’re in.   The solution?  Use a cut scene and remove your game scene while the cut scene is showing. This is a very common design pattern among games… pacman, angry birds, candy crush, etc.  If you don’t mind the flash, you could have an all black cut scene that simply removes the game and goes back to it as quickly as possible.

However the **right** thing to do is to use your scene:show()'s “will” phase and reposition your objects that may have moved back to their starting location.  A pain, yes, but if you’re doing:

player = display.newImageRect(“player.png”, 100, 100(

player.x = 100

player.y = 100

you are already writing the position code, just move those last two lines to the “will” phase and don’t worry about positioning them in scene:create().

For the last round of issues, you cannot call gotoScene() in a scene’s scene:hide() event because gotoScene is what triggers the scene:hide().  If you plan to use the sceneRemove() technique to reset a scene, then it has to happen before calling gotoScene() because it’s too late to remove the scene that gotoScene() is trying to go to.

I’m developing a multi-level game right now using the composer API.  I’m using the cut scene method to show the level score, number of stars, etc. and I can load my game scene over and over and over.   Create a scene that says “Try again”, show that message for a couple of seconds while you’re removing the game scene and go back to it.  Even better give your user a choice to try again or quit (goto your menu, etc.)  from that cut scene.

Rob

Now for your latest issue.  It sounds to me like scene1 is crashing for some reason.  While you may not have gotten a popup message, there should be something in your terminal/cmd window that says what the issue is.  That’s usually the cause of black screens and since you have an audio hit that your scene:create() is getting called, I think we need to look there.  Perhaps one of your parameter changes is causing the scene1 to error out.  Without seeing the code, we can only speculate.

As for the audio, you probably should delay audio playing until the scene has transitioned on the screen i.e. start it in scene:show()'s “did” phase.

Rob

Hi Rob,

Thanks for the thorough explanation. I’ll look into every of the possible alternate solutions you are proposing.

I tried already moving to a ‘bridge’ scene in between scene2 and scene1. It was a blank scene containing only the command ‘gotoscene1’. It works, I can take the ‘to black screen’ transition per say, but it only works for the first time. The second time I go to scene2 and then back, within the same app session, it returns still-black screen. No error returned. Is it just a matter of delaying a bit the call of scene1? If so, how long would be the ideal time??’

By the way I’m not insinuating this is a corona’s bug. At the level I’m programming I don’t have any ground to say what’s what. I Just came across this topic while looking for a solution to my ‘design’ problem, and the reload.lua is working great so far.

So for now I will keep that as a solution, if problems arise while testing on device I’ll let you guys know.

ingemar’s template works like a charm in my project: I also have a third scene in my program, let’s call it scene0. It’s an intro screen and it runs when application starts, it leads straight to scene1, then it’s just bouncing between scene1 and scene2.

But in scene2 you got a button that calls scene0 in order to rewatch the intro. Now, with ingemar’s code called in hide scene2, if I change option parameters and instead of going back straight to scene1 I go to scene0, when the intro ends and the program redirects to scene1 this gets reloaded properly with the adequate changes that had been set two scenes before. Just amazing!

There has to be an error.  You are not by any chance using a variable named “debug” are you?  “debug” is a global variable that’s used by the output system generate messages to the console log.

You can also put in some print statements in your scene’s create and show functions to see how far you’re getting too.

Rob

I’m not using any variables with that name or similar. But I realize now might the incongruence in the functioning of my program be due to the version of the corona version I’m using??? :

Version 2014.2189 (2014.3.6)

The current public build is 2393 for Windows and 2393a for Macs.  You should update for certain.

Rob

Was this ever resolved? Using a timer doesn’t seem to work for me…

I’ve implemented a blank “dummy” scene called reload.lua to get around the issue.

-- reload.lua -- -- blank scene used for reloading scenes -- local composer = require("composer") local scene = composer.newScene() -- ------------------------------------------------------------------------------- function scene:create(event) local sceneGroup = self.view end function scene:show(event) local sceneGroup = self.view local phase = event.phase if (phase == "did") then composer.removeHidden(true) composer.gotoScene(composer.getSceneName("previous")) end end function scene:hide(event) local sceneGroup = self.view local phase = event.phase end function scene:destroy(event) local sceneGroup = self.view end -- ------------------------------------------------------------------------------- scene:addEventListener("create", scene) scene:addEventListener("show", scene) scene:addEventListener("hide", scene) scene:addEventListener("destroy", scene) -- ------------------------------------------------------------------------------- return scene

Whenever you want to reload the current scene you just need to call 

composer.gotoScene(“reload”)

It works well, however it does have a small caveat: The screen can sometimes flash briefly between scene changes.

I’ve gotten around that by capturing the screen before reloading, and removing the capture after reloading.

Unfortunately it can’t be done within the reload module as the composer events fire in an order which doesn’t allow the capture to be effective.

Thanks ingemar for that. I ended up doing something similar. I was using 2189, but I take it this still hasn’t been fixed in the newest builds?

Not that I’m aware of…

hey guys,

tried to use this :

composer.removeScene(“test1”)  timer.performWithDelay(100,function() composer.gotoScene(“test1”) end)

inside of scenehide of the scene I’m leaving while going to “test1”, but I only get test1 destroyed and not recreated. I guess that’s the case 'cause I get a black screen and no error returned form the compiler. Any idea why??

The reason is that there’s an issue (bug?) with composer in that it can’t reload the current scene without going to another scene first.

If you implement the reload.lua scene I posted above it will work with the one small caveat I mentioned in that post.

thanks a lot ingemar.

at first your template wasn’t working for me as I was trying to call it from within the scene I want to be reloaded. But if I call it from another scene it runs just fine. No caveat to be reported…

Hope the bug get fixed soon…

Thanks again for your help !!

I’m not sure I follow. It should work being called from the current scene (that you want to reload)

I call composer.gotoScene(“reload”) and it reloads whatever scene is currently being displayed. I use it mostly for my scenes when a user changes language. The call to reload is in a button event handler.

So I didn’t misunderstood what I read on this topic: it IS supposed to be called from within the scene you want to reload…

well what I did was at first trying to call it from the scene:show will/did phase of the scene I wanted to reload, and it went all glitchy and crashed the corona simulator (and sublime text too ???). Ok, my mistake, I get it’s supposed to be called from the create scene function? but I can’t do that for the sake of the effect I’m trying to have on screen…

The only solution I managed to find (and keep in mind that I’m shooting in the dark on almost every thing I try since I’m just moving my very first steps in lua and programming in general…) was to call reload.lua from the will/did hide function of the other  scene I’m leaving to get into the one I want to reload.

Weirdly enough this solution reloads the scene I want perfectly, without any glitch occurring.

Now this is only being tested on the simulator so far. In a few days I will get my license to test on devices, so if you think problems might arise with the solution i adopted once on device please let me know in advance…

Thank you very much!!!

This shouldn’t be called within a “will/did show” or “will/did hide” event handler as those mean that you’re already entering/leaving a scene.

It shouldn’t even be called from within a create.

Can you explain what type of scene you’re trying to reload, and why?

well basically I have two scenes worth considering in this case: a start menu (scene1) from which you can access an option screen (scene2). Inside of the option screen there are a few parameters that can be changed to affect the appearance of the start menu. These parameters are checked on the hide scene phase of the scene2.

So basically what happens is: I enter scene2 by hitting a button on scene1. In scene2 change some parameters, hit back button to scene1, this button calls gotoscene1, in the hide phase of scene2 paramters get checked and reload.lua is called.

Results: i get scene1 reloaded with proper changes. Intuitively I’m prone to think this works cause by hitting backbutton we first are sent into scene1 and AFTER that reload is called (and paramaters passed and checked). But as I said concepts aren’t clear enough for me yet as I’m a neophite.

Before this expedient I couldn’t affect scene1 by changing parameters in scene2, no idea why, non of the two is an overlay scene…

What do you think about this??

OK. This is not a case for using my reload.lua as I see it.

In this case, I think you just need to call composer.removeHidden() in your “did show” event in scene2. When you go back to scene1 it should then be reloaded properly.

Just tried, but I need scene1 to be destroyed (-removed) or not based on the parameters that get checked on “did hide” of scene2 and iin “will/did hide” composer.removehidden doesn’t work.

So far using your reload.lua in" scene hide" of scene2 works great for my purpose, is it so bad if keep it this way…???

Why not do:

composer.removeScene(“scene1”)

composer.gotoScene(“scene1”)

from your button handler that closes scene2? 

Edited to add:  Your scene2’s scene:hide() function only happens once the scene starts transitioning away (“will phase”) and again once it’s done transitioning away (“did phase”).  By that point we have already initiated the scene change from a call to composer.gotoScene() and the scene you’re going to has already been constructed (or it’s reusing an existing scene).   You have to handle removing the destination scene before you call gotoScene() and that needs to happen somewhere in your active scene, not in it’s hide or destory events.

Rob