Skip to main content

Corona - Reloading a scene with transition effects

Corona has a quite nice composer library allowing you to work with scenes and moving between them without too much effort. If you're not already using the composer library in your Corona made app or game you should definitely start doing so right away.

One thing that I do miss in the composer lib is the option to reload the same scene while still making use of the provided transition effects such as "fade" and "slideRight". What happens when you call composer.gotoScene() to reload the current scene is that the scene will be hidden immediately without any transition effects before it's reloaded.

One way to create a smooth transition while reloading a scene is to handle it manually with a little help from the transition lib. For example, to create a fade out/in effect you could do something like the this:


  1. Create a black rectangle filling the entire screen. Make sure it's inserted above all other graphics. Set it's alpha to 0 to make it invisible.
  2. Start a transition effect that gradually increases the alpha of the rect to 1.
  3. When the transition completes, reload the scene with composer.gotoScene() and make sure that the same rectangle is visible with full alpha.
  4. Start another transition effect that reduces the alpha of the rect to 0 to uncover the other graphics.
Here's an example from Ice Trap when the player clicks the retry button. (The fade effect has been slowed down to make it easier to see here.)

Manually created fade out/in transition.

Another way to enable transition effects while reloading a scene is to create a separate "transition scene". Again, this requires quite a lot of code to accomplish but don't worry: I've already done this and here's my Lua source code for the transition scene:


local composer = require( "composer" )
local scene = composer.newScene()

local bg

function scene:create( event )
    local sceneGroup = self.view
    bg = display.newRect(display.contentCenterX, display.contentCenterY, display.contentWidth, display.contentHeight)
    sceneGroup:insert(bg)
end

function scene:show(event)
    local sceneGroup = self.view
    local phase = event.phase

    local params = event.params or {}

    if ( phase == "will" ) then
        local bgColor = params.bgColor or { 0, 0, 0, 1 }
        bg:setFillColor(unpack(bgColor))
    elseif ( phase == "did" ) then
        local options = {
            effect = params.effect or "crossFade",
            time = params.time or 250,
            params = params.nextParams or {},
        }

        local delayMs = (params.delay ~= nil) and params.delay or 500

        timer.performWithDelay( delayMs, function()
            composer.gotoScene(params.nextSceneName, options)
        end)
    end
end

function scene:hide( event )
    if ( phase == "will" ) then
    elseif ( phase == "did" ) then
    end
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

Save the file above as "scene-transition.lua" and then you'll be able to use it like in this example where I've replaced the fade out effect from Ice Trap with a slide effect:


local options = {
    time = 500,
    effect = "slideRight",
    params = {
        time = 500,
        delay = 200,
        effect = "fromRight",
        nextSceneName = "scene-gameplay",
        --bgColor = { 0, 1, 1, 1 },
        nextParams = {
            chapterNumber = _currentChapterNumber,
            levelNumber = _currentLevelNumber,
            isRetry = true,
        }
    }
}
composer.gotoScene("scene-transition", options)

Transition scene with slide effect

So, what's going on in the code above? 

  1. The time and effect properties are applied to the initial transition, i.e. from the current scene to scene-transition.
  2. params.time and params.effect are applied to the second transition, i.e. from scene-transition to params.nextSceneName.
  3. params.delay can be used if you want a delay between the first and second transition.
  4. params.nextSceneName is the name of the scene you want to transition back to.
  5. params.nextParams are your custom params that will be forwarded to the scene specified by params.nextSceneName, and will be received as event.params in the show()/hide() functions of that scene.
  6. params.bgColor can be used to set the background color of the transition scene to anything else than black.
  7. If you want to be able to customize other graphics than the background color you can add your own properties similar to bgColor and handle them accordingly in scene-transition.

Using a transition scene works great for most transition effects, but not for fade/crossFade. Now why is that? It's because all fade/crossFade does is change the alpha of the display objects/groups of the scene(s). This means that if you have several layers of graphics (which you normally do) things will shine through where they shouldn't.

Have a look at the scene transition fade effect below and compare to the manual fade effect shown above and you'll probably spot the difference. This just doesn't look very good in my opinion, so when it comes to fade out I'll just stick to doing it manually. (I might write a separate blog post on that topic later.)

Transition scene with fade effect. Ouch, that doesn't look good.
Hope this all made sense to you and that you can make some use of the scene-transition code in your game.

Comments