Black Screen issue with composer.gotoScene

We created our own “change_scene” function to change scenes because using the “fade” parameter on gotoScene doesn’t fade everything out the way we like. Our version fades a black screen from 0 to 1 alpha, does a gotoScene, then fades the black screen alpha from 1 to 0 again.

The problem is that sometimes the game hangs while displaying the black screen. We don’t know why but we suspect it’s because of file I/O happening in the background. Here’s our code:

--[[------------------------------------------ change scenes a better way IN: scene = name of lua file t = time in milliseconds options = {} with composer options in it --------------------------------------------]] current\_scene\_id = nil local scene\_change\_transition function change\_scene( scene, t, options ) if scene\_change\_transition then log( "Unable to change scene to " .. scene .. ". There is already an active scene transition!", err, 5 ) return end local w, h = display.contentWidth, display.contentHeight local black = display.newRect( \_XC, \_YC, w \* 2, h \* 2 ) black:setFillColor( 0,0,0 ) -- black.anchorX, black.anchorY = 0,0 black.alpha = 0 -- fade to black if debug\_no\_fade then -- change scenes instantly local o = options if not o then o = { } end o.effect = nil log( "change\_scene instant = "..scene ) -- log( "change\_scene: mid fade." ) -- print\_r( o ) composer.gotoScene( scene, o ) current\_scene\_id = scene return end log( "change\_scene: begin fade." ) scene\_change\_transition = transition.to( black, { alpha = 1.0, time = t / 2, onComplete = function() scene\_change\_transition = nil -- change scenes instantly local o = options if not o then o = { } end o.effect = nil log( "globals: change\_scene = "..scene ) -- log( "change\_scene: mid fade." ) -- print\_r( o ) composer.gotoScene( scene, o ) current\_scene\_id = scene -- and fade back in & delete the black transition.to( black, { alpha = 0, time = t / 2, onComplete = function() -- scene\_change\_transition = nil -- log( "change\_scene: end fade." ) display.remove( black ) end } ) end } ) end

John,

Hi.  I made some significant mods to your code and I hope I didn’t misinterpret your goal. 

The following code( may contain typos):

  1. Is a module that when required (once) will extend composer.* by adding a function called ‘customFade’

  2. ‘customFade()’ retains your ‘debug_no_fade’ code.

3. ‘customFade()’ takes three arguments:

> scene - Path to scene file

> options - Optional options table to pass to gotoScene()

> t - Optional time in milliseconds (defaults to 1000)

3. ‘customFade()’ operates as follows

a. If ‘debug_no_fade’ is ‘true’ it clears the current effect and goes right to the scene using your options.

b. Otherwise…

b1 - It creates a transparent black rectangle covering the screen.

b2 - It defines two methods: fadeUp, fadeDown, each is responsible for, as you might expect, fading the rectangle up to fully opaque and then fully transparent.  Each fade takes time t/2

b3 - The code uses transitions and onComplete substitutions to hand off responsibility for these consecutive actions.

b4 - After fading up, the composer scene is swapped and the fade down starts

b5 - After the fade down completes, the black rectangle autodestructs.

This should never fail to work.  If it does, please post back.

-- Place this in a module and require it once to add new 'gotoScene' function to composer.\* \_G.current\_scene\_id = nil local cx = display.contentCenterX local cy = display.contentCenterY local fullw = display.actualContentWidth, local fullh = display.actualContentHeight -- Extend composer.\* library w/ new function function composer.customFade( scene, options, t ) t = t or 1000 options = options or {} if debug\_no\_fade then options.effect = nil composer.gotoScene( scene, options ) \_G.current\_scene\_id = scene return end local black = display.newRect( cx, cy, fullw, fullh ) black:setFillColor( 0 ) black.alpha = 0 function black.fadeDown( self ) -- Change scene now options.effect = nil options.time = nil composer.gotoScene( scene, options ) \_G.current\_scene\_id = scene -- Fade out and destroy black self.onComplete = display.remove transition.to( self, { alpha = 0, time = t/2, onComplete = self } ) end function black.fadeUp( self ) self.onComplete = self.fadeDown transition.to( self, { alpha = 1, time = t/2, onComplete = self } ) end -- Start fade black:fadeUp() end

Oh, once you require the file containing the code you can call it anywhere from composer.*

composer.customFade( ... )

Thanks for this alternate! What did you see in our code that could create the black screen hanging issue?

Hi.  I didn’t see anything specifically, but my sense was there might be a tricky scope and visibility issue cropping up.  

Perhaps the transition getting cancelled, or … I’m just not sure.  

Since I couldn’t figure out why it was failing, I approached it with an alternate solution that got rid of:

  • References to black after the start of the transition sequence.
  • Transition handle removed from file-level local space (actually, just removed entirely).

Sorry I don’t have better insights for you on the original issue.  

I understand how important it is to discover ‘original cause of failure’ both to resolve it and avoid it in the future.

-Ed

Corona is single threaded and therefore any code that takes a while to run will block screen updates.  If you have heavy I/O or lots of object creation happening in composer.gotoScene( scene ) then you will see the black screen for as long as this takes to execute.

I use these two simple helper functions to time various operations to see how long they are taking.  Call startTimer() once and then stopTimer() as many times after to profile individual lines or blocks of code.

local startTime = nil function startTimer()   startTime = system.getTimer() end function stopTimer()   local elapsedTime = system.getTimer() - startTime   print("Time = "..math.round(elapsedTime).." ms") end

Adrian

We don’t have a slowdown.

We have a game hanging issue, and it’s most likely tied to saving a file just before doing a transition.

Is your new scene actually being loaded?  Anything useful in the log?

Check for things like transition.cancel() in code somewhere that could be stopping the onComplete handler firing.

Nothing useful in log. We have no empty transition.cancel() calls in our code.

It’s an interesting bug…

What I do with these weird bugs is sprinkle lots of print(“1”), print(“2”), print(“3”), etc. calls in and see what numbers print (or don’t print) when it goes wrong.

Sometimes it’s the only way to localise a problem when on trappable errors are being raised.

The problem is that this doesn’t happen to us, just to players on PCs. We saw it a couple times over two years but couldn’t figure out what is causing it and players have no idea why because there’s no error dialog popping up.

We also get rid of all print() functions for a published version for speed, so they wouldn’t show up in a console.

Oh debugging installed apps is problematic unless you can log all errors to a server somewhere.

I don’t strip my print statements but do this, which nulls the print on a public build

if not \_debug then print = function() end end

Yes, that’s basically what we do.

John, is this still a problem?

Thanks

Rob

Hi Rob – we won’t know until we put out another release. We have almost never seen this bug. We never develop with Windows, so that makes it tough to see the problem.

John,

Hi.  I made some significant mods to your code and I hope I didn’t misinterpret your goal. 

The following code( may contain typos):

  1. Is a module that when required (once) will extend composer.* by adding a function called ‘customFade’

  2. ‘customFade()’ retains your ‘debug_no_fade’ code.

3. ‘customFade()’ takes three arguments:

> scene - Path to scene file

> options - Optional options table to pass to gotoScene()

> t - Optional time in milliseconds (defaults to 1000)

3. ‘customFade()’ operates as follows

a. If ‘debug_no_fade’ is ‘true’ it clears the current effect and goes right to the scene using your options.

b. Otherwise…

b1 - It creates a transparent black rectangle covering the screen.

b2 - It defines two methods: fadeUp, fadeDown, each is responsible for, as you might expect, fading the rectangle up to fully opaque and then fully transparent.  Each fade takes time t/2

b3 - The code uses transitions and onComplete substitutions to hand off responsibility for these consecutive actions.

b4 - After fading up, the composer scene is swapped and the fade down starts

b5 - After the fade down completes, the black rectangle autodestructs.

This should never fail to work.  If it does, please post back.

-- Place this in a module and require it once to add new 'gotoScene' function to composer.\* \_G.current\_scene\_id = nil local cx = display.contentCenterX local cy = display.contentCenterY local fullw = display.actualContentWidth, local fullh = display.actualContentHeight -- Extend composer.\* library w/ new function function composer.customFade( scene, options, t ) t = t or 1000 options = options or {} if debug\_no\_fade then options.effect = nil composer.gotoScene( scene, options ) \_G.current\_scene\_id = scene return end local black = display.newRect( cx, cy, fullw, fullh ) black:setFillColor( 0 ) black.alpha = 0 function black.fadeDown( self ) -- Change scene now options.effect = nil options.time = nil composer.gotoScene( scene, options ) \_G.current\_scene\_id = scene -- Fade out and destroy black self.onComplete = display.remove transition.to( self, { alpha = 0, time = t/2, onComplete = self } ) end function black.fadeUp( self ) self.onComplete = self.fadeDown transition.to( self, { alpha = 1, time = t/2, onComplete = self } ) end -- Start fade black:fadeUp() end

Oh, once you require the file containing the code you can call it anywhere from composer.*

composer.customFade( ... )

Thanks for this alternate! What did you see in our code that could create the black screen hanging issue?

Hi.  I didn’t see anything specifically, but my sense was there might be a tricky scope and visibility issue cropping up.  

Perhaps the transition getting cancelled, or … I’m just not sure.  

Since I couldn’t figure out why it was failing, I approached it with an alternate solution that got rid of:

  • References to black after the start of the transition sequence.
  • Transition handle removed from file-level local space (actually, just removed entirely).

Sorry I don’t have better insights for you on the original issue.  

I understand how important it is to discover ‘original cause of failure’ both to resolve it and avoid it in the future.

-Ed

Corona is single threaded and therefore any code that takes a while to run will block screen updates.  If you have heavy I/O or lots of object creation happening in composer.gotoScene( scene ) then you will see the black screen for as long as this takes to execute.

I use these two simple helper functions to time various operations to see how long they are taking.  Call startTimer() once and then stopTimer() as many times after to profile individual lines or blocks of code.

local startTime = nil function startTimer()   startTime = system.getTimer() end function stopTimer()   local elapsedTime = system.getTimer() - startTime   print("Time = "..math.round(elapsedTime).." ms") end

Adrian