Composer Guide (API)

The Composer library is the official scene (screen) creation and management system in Corona SDK. This library provides developers with an easy way to create and transition between individual scenes.

Understanding Scenes

Scenes are basically just Lua files, existing as separate .lua files in your project. There is, however, some additional structural setup that you must obey so that Composer can treat them as scenes. This includes two lines to initialize the scene, four listener functions to handle the events that Composer generates, four lines of code to initialize these listener functions, and a basic return to associate the scene with Composer. Please see the sample scene template located on the Composer API index page.

Because scenes are Lua files that are loaded into memory via the Lua require() function, they only get loaded once. This is important to understand, since code that resides outside of the scene's listener functions will not be re-executed every time the scene is shown or hidden.

The scene object itself is created by calling composer.newScene(). This object holds important data for the scene that Composer must access. For the developer, the most important aspect is the scene's view (self.view) — this is the display group to which all of the scene's visual content will be added. For example, display objects like images and vector objects must be inserted into the view, along with interactive objects like widgets. This is a critical aspect of Composer.

A scene's self.view group is stacked at the bottom of the display hierarchy. If you create display objects but do not insert them into the scene's view group, they will reside in front of the Composer stage.

Life Cycle

The Composer lifecycle starts within main.lua. However, main.lua itself is not a Composer scene — it's merely used for initialization code, then it launches the first scene via composer.gotoScene(). In this call, specify the name of the scene (file) to be loaded, minus the .lua extension:

local composer = require( "composer" )

-- Code to initialize your app

-- Assumes that "scene1.lua" exists and is configured as a Composer scene
composer.gotoScene( "scene1" )

Once loaded, the scene's life cycle begins and it follows this basic flow:

  1. The scene file is loaded and a Composer scene object is created.
  2. The scene's view is created (if it does not already exist) and a create event is dispatched to the scene's local scene:create() function.
  3. If a scene already exists on the screen, it is hidden and, optionally, recycled or removed.
  4. The new scene is shown.

Scene Events

Four different life cycle functions handle Composer-generated events. The sample scene template located on the Composer API index page illustrates the basic setup for these functions.

scene:create()

When a scene is loaded and it does not have an associated self.view established, Composer will dispatch an event to the scene's local scene:create() function. If the scene's view already exists, the create event is skipped because, by default, Composer tries to keep the scene's view in memory, assuming you will return to the scene at some point. Thus, the scene:create() function may not be executed every time the scene is shown.

local composer = require( "composer" )

local scene = composer.newScene()

function scene:create( event )

    local sceneGroup = self.view

    -- Initialize the scene here.
    -- Example: add display objects to "sceneGroup", add touch listeners, etc.
end

scene:addEventListener( "create", scene )

return scene

The scene:create() function is an opportune time to create user interface objects and other display objects needed for the scene, including buttons, text, graphics, and other objects that should show when the scene comes on screen. This does not include native objects like input text fields — these should be shown in the scene:show() function instead.

Important

If non-native display objects should be part of the scene and managed by Composer — for example, cleaned up when the scene is removed — these objects must be inserted into the scene's self.view. Note that self.view is assigned to the local variable sceneGroup for easy reference.

local composer = require( "composer" )

local scene = composer.newScene()

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

    local background = display.newRect( display.contentCenterX, display.contentCenterY, display.contentWidth, display.contentHeight )
    background:setFillColor( 1, 0, 0.2 )

    sceneGroup:insert( background )
end

scene:addEventListener( "create", scene )

return scene

Upon creation, the scene's view is hidden. Depending on your scene transition options, it may be underneath the existing scene or even off the screen. Because the scene is not yet visible, you should not start any timers or physics object activity at this time. Likewise, if you're using scene-specific audio, you may load the audio into memory here, but you should not play it.

scene:show()

This function is triggered twice each time the scene is shown: once just before the scene is going to be shown and again after the scene is fully on screen.

local composer = require( "composer" )

local scene = composer.newScene()

function scene:show( event )

    local sceneGroup = self.view
    local phase = event.phase

    if ( phase == "will" ) then
        -- Called when the scene is still off screen (but is about to come on screen).
    elseif ( phase == "did" ) then
        -- Called when the scene is now on screen.
        -- Insert code here to make the scene come alive.
        -- Example: start timers, begin animation, play audio, etc.
    end
end

scene:addEventListener( "show", scene )

return scene

As you can see, the event passed to the scene:show() function includes a phase parameter equal to one of two values: "will" or "did".

  • The "will" phase occurs just before the scene comes on screen. This is a great opportunity to reposition objects that may have moved from their intended starting position since the scene was last shown (like restarting a game level).

  • The "did" phase occurs when the scene is fully on screen. This is the proper place to create native objects like text fields, maps, etc. since they cannot be inserted into display groups, and thus cannot reside in the scene's self.view group. The "did" phase is also a good opportunity to start transitions/timers, play scene-specific music that was loaded in scene:create(), and start the physics simulation if you're using Corona's physics engine.

scene:hide()

This function is triggered twice each time the scene is hidden: once when the scene is about to go off screen and again after the scene goes fully off screen.

local composer = require( "composer" )

local scene = composer.newScene()

function scene:hide( event )

    local sceneGroup = self.view
    local phase = event.phase

    if ( event.phase == "will" ) then
        -- Called when the scene is on screen (but is about to go off screen).
        -- Insert code here to "pause" the scene.
        -- Example: stop timers, stop animation, stop audio, etc.
    elseif ( phase == "did" ) then
        -- Called immediately after scene goes off screen.
    end
end

scene:addEventListener( "hide", scene )

return scene

Like show, the event passed to the scene:hide() function includes a phase parameter equal to one of two values: "will" or "did".

  • The "will" phase occurs when the scene is about to go off screen. This is a great opportunity to pause or stop physics, cancel timers and transitions, remove native objects, and stop scene-specific audio that was played in scene:show().

  • The "did" phase of scene:hide() will not require many actions, although you could remove the scene's self.view group or even remove the scene permanently.

scene:destroy()

This function is where Composer cleans up the scene's display objects (any non-native display object that you inserted into the scene's self.view group or a child group of it). scene:destroy() is called just prior to this clean up. Here you can "undo" things that you did in scene:create() which are not related to the scene's display objects, for example, dispose of scene-specific audio. See Scene Management for more information.

function scene:destroy( event )

    local sceneGroup = self.view

    -- Called prior to the removal of scene's view ("sceneGroup").
    -- Insert code here to clean up the scene.
    -- Example: remove native objects, save state, etc.
end

Because Composer tries to keep scenes in memory for performance reasons, this function is only called in these circumstances:

  • If an explicit call is made to composer.removeScene().
  • If Composer is set to automatically remove the scene's view.
  • If the app receives a low memory warning from the OS.

If you loaded audio files into memory inside the scene:create() function, scene:destroy() is a good time to dispose of the audio. Similarly, if you opened access to a database in scene:create(), you may close it here.

Scene Template

The following template can be used to create new scene files. Note that this template includes listener functions for all potential events in the scene, but you only need to include listeners for the events that you want to handle.

local composer = require( "composer" )

local scene = composer.newScene()

-- -----------------------------------------------------------------------------------------------------------------
-- All code outside of the listener functions will only be executed ONCE unless "composer.removeScene()" is called.
-- -----------------------------------------------------------------------------------------------------------------

-- local forward references should go here

-- -------------------------------------------------------------------------------


-- "scene:create()"
function scene:create( event )

    local sceneGroup = self.view

    -- Initialize the scene here.
    -- Example: add display objects to "sceneGroup", add touch listeners, etc.
end


-- "scene:show()"
function scene:show( event )

    local sceneGroup = self.view
    local phase = event.phase

    if ( phase == "will" ) then
        -- Called when the scene is still off screen (but is about to come on screen).
    elseif ( phase == "did" ) then
        -- Called when the scene is now on screen.
        -- Insert code here to make the scene come alive.
        -- Example: start timers, begin animation, play audio, etc.
    end
end


-- "scene:hide()"
function scene:hide( event )

    local sceneGroup = self.view
    local phase = event.phase

    if ( phase == "will" ) then
        -- Called when the scene is on screen (but is about to go off screen).
        -- Insert code here to "pause" the scene.
        -- Example: stop timers, stop animation, stop audio, etc.
    elseif ( phase == "did" ) then
        -- Called immediately after scene goes off screen.
    end
end


-- "scene:destroy()"
function scene:destroy( event )

    local sceneGroup = self.view

    -- Called prior to the removal of scene's view ("sceneGroup").
    -- Insert code here to clean up the scene.
    -- Example: remove display objects, save state, etc.
end


-- -------------------------------------------------------------------------------

-- Listener setup
scene:addEventListener( "create", scene )
scene:addEventListener( "show", scene )
scene:addEventListener( "hide", scene )
scene:addEventListener( "destroy", scene )

-- -------------------------------------------------------------------------------

return scene

The first line localizes the Composer library for the scene and the next line creates the scene's view. After this, you should include any scene-wide functions and forward-declare local variables that will be used in the scene. Because Lua files are only loaded once, variables that you set here will not be reset when you revisit the scene unless you take explicit steps to unload the file.

After this are the four functions to handle events generated for the scene. Then, event listeners are declared to handle scene events and, finally, the scene object is returned to Composer.

Note that within the listener functions, self.view is assigned to the local variable sceneGroup for easy reference. If you need to reference the scene's view outside of these functions, just use scene.view.

Going to Scenes

Once you create scene files, you need a method to access them. In Composer, this is accomplished via composer.gotoScene( sceneName ). The sceneName parameter corresponds to the name of the Lua file, without the .lua extension. For example, if you have a scene file named menu.lua, access that scene with:

composer.gotoScene( "menu" )

In addition, there are several parameters which you can optionally pass to composer.gotoScene() to control the transition as well as provide data to the scene. You can do the following:

local options = {
    effect = "fade",
    time = 500,
    params = {
        someKey = "someValue",
        someOtherKey = 10
    }
}
composer.gotoScene( "menu", options )

In this example, the menu.lua scene will transition on screen using a "fade" effect over the span of 500 milliseconds. An optional table of parameters is also passed in. To access this data from the menu.lua scene, access the event.params table in the target scene's scene:create() or scene:show() function:

function scene:create( event )
    local sceneGroup = self.view
    local params = event.params

    print( params.someKey )
    print( params.someOtherKey )
end
Important

Because Composer keeps the current scene's view in memory by default, the scene:create() function will only be called when the scene is first created. Thus, if you pass a table of parameters and you attempt to access it in the scene:create() function as event.params, it will only be available when the scene is first created.

Transition Effects

The following string values are supported for the effect key of the options table:

  • "fade"
  • "crossFade"
  • "zoomOutIn"
  • "zoomOutInFade"
  • "zoomInOut"
  • "zoomInOutFade"
  • "flip"
  • "flipFadeOutIn"
  • "zoomOutInRotate"
  • "zoomOutInFadeRotate"
  • "zoomInOutRotate"
  • "zoomInOutFadeRotate"
  • "fromRight" — over current scene
  • "fromLeft" — over current scene
  • "fromTop" — over current scene
  • "fromBottom" — over current scene
  • "slideLeft" — pushes current scene off
  • "slideRight" — pushes current scene off
  • "slideDown" — pushes current scene off
  • "slideUp" — pushes current scene off

Scene Management

One of the key features of Composer is that, for the most part, it manages display objects automatically, assuming you insert them into the scene's view group:

function scene:create( event )

    local sceneGroup = self.view

    local menuBack = display.newRect( display.contentCenterX, display.contentCenterY, 280, 360 )
    sceneGroup:insert( menuBack )
end

Upon this scene being recycled or removed, Composer will handle the proper disposal of the menuBack object, along with other objects that were inserted into the scene's view group or a child group of it. Touch, tap, and collision listeners applied to these objects will also be removed.

Important

Just like removing display objects outside of Composer, you're still responsible for the following when you're finished with a scene:

Auto-Recycling Scenes

By default, when changing scenes, Composer keeps the current scene's view in memory, which can improve performance if you access the same scenes frequently. If you wish to recycle the current scene when changing to a new scene (remove the current scene's self.view), you can set the composer.recycleOnSceneChange property to true.

composer.recycleOnSceneChange = true

To revert to the default behavior where scene views are retained (but hidden from view), set the property to false:

composer.recycleOnSceneChange = false

It's important to understand that neither of these conditions will unload the scene from Lua memory. To explicitly remove a scene from memory, use the composer.removeScene() call. See the next section for details.

Removing Scenes

If you're completely finished with a scene and don't intend to access it again, you may remove it via the following API:

composer.removeScene( sceneName [, shouldRecycle])

For instance, if you want to remove both the view and the scene object for menu.lua, call:

composer.removeScene( "menu" )

Optionally, you can pass the value of shouldRecycle as true. This will remove the scene's view from the display hierarchy, but it will remain in Lua memory.

composer.removeScene( "menu", true )

Reloading Scenes

Reloading scenes requires a slightly different approach. Many people want to create and position all of the scene display objects in the scene:create() function. However, if you reload the scene from itself, this function is not called again because the scene's view still exists. Objects that may have moved (i.e. characters in a game) will remain in place when you reload the scene, and variables defined outside of the listener functions will remain at their current values. For example, if you set a score variable to 0 outside of the listener functions, it will not reset to that value when you reload the scene.

Even if you destroy the scene's view, certain variables will not reset. While the scene:create() will re-execute in that case, any code that was executed outside of the listener functions will not re-execute.

The best practice for reloading scenes is to use an intermediate scene. In games, this is often referred to as a "cutscene" and it may show the user a summary of their performance, a menu of options like "replay" and "exit", etc. Using an intermediary cutscene, you can manually remove the scene that you want to reload, resulting in a fresh start when you load it again. However, you may still need to reset variables and reposition objects in the "will" phase of the scene:show() function.

If you want to skip the cutscene approach, you can reload a scene from itself by calling:

local currScene = composer.getSceneName( "current" )
composer.gotoScene( currScene )

Overlay Scenes

Composer allows you to have one overlay scene. This is a scene that gets loaded on top of the active scene (the parent scene). An overlay scene is constructed like any other Composer scene.

Showing an Overlay

To show an overlay, call it via the composer.showOverlay() function. Because an overlay scene may not cover the entire screen, users may potentially interact with the parent scene underneath. To prevent this, set the isModal parameter to true in the options table. This prevents touch/tap events from passing through the overlay scene to the parent scene.

local options = {
    effect = "fade",
    time = 500,
    isModal = true
}
composer.showOverlay( "inventory", options )

When the overlay is loaded, it behaves exactly like any other scene in regards to its event handling. That is, scene:create(), scene:show(), scene:hide() and scene:destroy() will be called with the same rules as other scenes.

Hiding an Overlay

To hide an overlay and return to the parent scene, use the composer.hideOverlay() call, for example:

composer.hideOverlay( "fromBottom", 400 )

This can be called from the overlay scene, from the parent scene, or from some event handler like an Android "back" key handler. Attempting to go to another scene via composer.gotoScene() will automatically hide the overlay as well.

Accessing the Parent Scene

When showing or hiding the overlay, you may need to perform actions in the parent scene. For instance, you may need to gather some input or selection from the overlay and update some aspect of the parent scene. In Composer, the overlay scene has access to the parent's scene object via event.parent. This allows you to access functions/methods in the parent scene and communicate with the parent when the overlay scene is shown or hidden. For example:

------------------------------------------------------------------------------
-- In "scene1.lua" (parent scene)
------------------------------------------------------------------------------
local composer = require( "composer" )

local scene = composer.newScene()

-- Custom function for resuming the game (from pause state)
function scene:resumeGame()
    --code to resume game
end

-- Options table for the overlay scene "pause.lua"
local options = {
    isModal = true,
    effect = "fade",
    time = 400,
    params = {
        sampleVar = "my sample variable"
    }
}

-- By some method (a pause button, for example), show the overlay
composer.showOverlay( "pause", options )

return scene

------------------------------------------------------------------------------
-- In "pause.lua"
------------------------------------------------------------------------------
local composer = require( "composer" )

local scene = composer.newScene()

function scene:hide( event )
    local sceneGroup = self.view
    local phase = event.phase
    local parent = event.parent  --reference to the parent scene object

    if ( phase == "will" ) then
        -- Call the "resumeGame()" function in the parent scene
        parent:resumeGame()
    end
end

-- By some method (a "resume" button, for example), hide the overlay
composer.hideOverlay( "fade", 400 )

scene:addEventListener( "hide", scene )
return scene

Other Methods/Properties

Composer features a few convenience methods and properties to assist with your scene management.

Setting/Getting Variables

You may store key-value pairs internally within the Composer module which should assist with accessing data between scenes. To set a variable that may be accessed from another Composer scene, use the composer.setVariable() function, for example:

composer.setVariable( "gameLevel", 1 )
composer.setVariable( "playerName", "Zorron" )

Then, to retrieve a variable from another scene:

local playerName = composer.getVariable( "playerName" )

Getting a Scene Name

To get the name of a scene, use the composer.getSceneName() function and pass in one of the following string values as the sole required parameter:

  • "current" — gets the current scene name
  • "previous" — gets the name of the scene which was previously shown
  • "overlay" — gets the name of the overlay scene (if it's showing)
local currScene = composer.getSceneName( "current" )
local prevScene = composer.getSceneName( "previous" )
local overlayScene = composer.getSceneName( "overlay" )

Composer Stage Object

The composer.stage property returns a reference to the top-level Composer display group which all scene views are inserted into. This can be considered as the "Composer scene layer" and it's useful if you need to place display objects above or below all Composer scenes, even during transition effects. For example:

  • A background image that is displayed behind all Composer scenes and remains static, even during scene transitions.
  • A tab or UI bar that appears above all Composer scenes.
  • HUD (heads-up display) elements such as health, score, etc.

Debugging

The composer.isDebug property toggles "Composer Debug Mode" which, if set to true, prints useful debugging information to the Corona Terminal in certain situations. This should be set to false (default) before building the project for deployment.