Phaser uses the concept of Scenes to allow you to divide your game up into logical sections. A Scene can be as large, or as small, as you like. Typical uses for a Scene would be a Loading Screen, a Main Menu, a Game Level, a Boss Fight, an in-game Item Shop, a High Score Table, etc. You can have as many Scenes in your game as you like. When you are starting out, you'll probably only have one or two, but as your game grows in complexity, you'll find yourself adding more.
It's important to understand that you do not have to have one Scene per file. You can, if you like, but it's not a requirement. You can have all of your Scenes in a single file, or you can have one file per Scene. It's entirely up to you.
Internally, there is a Scene Manager. This Manager is what you interact with when you add, remove or switch between Scenes. It's also responsible for updating and rendering the Scenes. You have full control over the order in which the Scenes are rendered in your game. For example, it's a common practise to have a Scene dedicated entirely to handling the UI for your game, that is rendered above all other Scenes.
We will look at the life-cycle of a Scene in more detail later, but for now it's worth understanding that you can pause, resume and sleep a Scene at will, and that you can easily run multiple Scenes at the same time.
What is a Scene?
In Phaser 2 there was the concept of a Game World and States. A State allowed you a view into the Game World and only one State could ever be running at once. The State had access to all kinds of systems, such as a Camera, Tweens and the Game Object Factory. When you used a command like this.add
from within a State, you were talking to the global Game Object Factory. Once you were done with a State you moved to the next one. States were often split up into logical sections, such as a Preloader State, a Main Menu State and so on.
In Phaser 3 we changed our approach to this entirely. The first change was that States were renamed to Scenes. This was to avoid ambiguity with 'state machines', which are commonly used in games. The global State Manager was also replaced with a Scene Manager. The two terms are conceptually interchangeable, but Scene is the correct term to use and expect to see in Phaser 3. If you find any reference to States in code or a tutorial you've found online, it's for Phaser 2 and likely won't work in Phaser 3. The changes were not just in the naming of the classes though, they run much deeper than this.
Rather than Phaser managing one 'Game World' there is now no World object at all. Instead of Scenes just having references to all of the systems that the Phaser.Game instance owns, they now own them themselves. You can think of Scenes as being almost entirely self-contained Game Worlds in their own right. They manage their own input, tweens, game objects, display list, cameras and more. A Tween created in Scene A is completely separate to one created in Scene B.
Scenes now control everything using their own instances of plugins. In fact the only thing that the Game itself is responsible for is passing on global DOM based events to the Scenes, such as when to update (from RequestAnimationFrame) or core input events from the Input Manager. The handling of these events are done by the Scenes themselves though.
What does Game still control?
There are a few systems which are inherently global in their nature and therefore belong to the Game instance. These are:
- The Renderer. There is only one instance of either the Canvas or WebGL Renderer per Game, and it's a property of the Game object.
- The Animation Manager. Animations in Phaser 3 are global and no longer bound to a specific Game Object. You can create many different animations, which are all stored in the Animation Manager, and then any Game Object, in any Scene, can use them without duplicating lots of data and wasting memory.
- The Cache. When files are loaded by a Scene they are placed into a global cache. So if Scene A loads an XML file, that XML data can be retrieved by any Scene as the cache is global.
- The Registry. Every Scene has its own instance of the DataManager, but the Game also has one which you can reference from a Scene via the property
registry
. This allows for easy cross-Scene data exchange (as we'll see later in this guide) - The Input Manager. The Input Manager does very little in Phaser 3. It just monitors and processes DOM-level input events, such as those raised from pointers, gamepads or the keyboard. Every Scene has its own Input Plugin, which is what does all of the real work with the events, and is what you interface with when calling
this.input
from a Scene. - The Scene Manager. Responsible for creating, managing and updating all of the Scenes in your game.
- The Device Inspector. Allows you to poll for features supported by the browser and hardware your game is running on. The Device class is actual a singleton and only runs once per browser, so isn't directly bound to your Game instance (i.e. if you had 2 game instances running on the same page, they would share the global Device inspector)
- The Sound Manager. Like rendering, sounds are managed by a global system in Phaser 3. You can create Sound objects, markers, and so on from your Scenes, but there is only one manager running at once to avoid resource allocation issues.
- The TimeStep. This plugin manages the Request Animation Frame loop and updates the core Game, causing it to step. You rarely need to interact with this directly.
Everything else, such as Tweens, Physics, Game Objects and Input handling are all managed through Scene level plugins. The Game is now only used for the timestep and the systems that are truly global, which wouldn't have made sense to duplicate per Scene.
What happens when you create a Scene
There are lots of different ways to create a Scene but the fundamental approach is the same in all of them. A Scene consists of a Scene Configuration object and a bunch of functions. Here we'll create an example Scene using an ES6 approach:
class MyScene extends Phaser.Scene {
constructor ()
{
super('MyFirstScene');
}
preload ()
{
this.load.image('logo', 'assets/sprites/logo.png');
}
create ()
{
this.add.image(400, 300, 'logo');
}
}
We extend the Phaser.Scene
object and give it a unique key (in this case MyFirstScene
) in the constructor. There are two methods: preload
and create
. The first loads an image, the second displays it. You don't have to have a preload
method, the only requirement to be a valid Scene is a create
method as this is the global entry-point of all Scenes in Phaser 3.
Here is the exact same thing using ES5:
var MyScene = new Phaser.Scene('MyFirstScene');
MyScene.preload = function ()
{
this.load.image('logo', 'assets/sprites/logo.png');
}
MyScene.create = function ()
{
this.add.image(400, 300, 'logo');
}
Game configuration
Creating the Scene on its own isn't enough though, it also needs to be added to the Game. You can do this when the Game is created, using the game config like this:
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
scene: MyScene
};
var game = new Phaser.Game(config);
The Scene has been passed in via the scene
property. This can also be an array of Scenes if you have more than one. This is exactly the same regardless of ES5 or ES6.
If you've looked at any of the Phaser 3 examples you'll probably have seen a completely different method of creating a Scene that looks more like this:
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
scene: {
preload: preload,
create: create
}
};
var game = new Phaser.Game(config);
function preload ()
{
this.load.image('logo', 'assets/sprites/logo.png');
}
function create ()
{
this.add.image(400, 300, 'logo');
}
So hang on a minute, in the ES6 and ES5 Scenes we passed an instance of them in the game config, but in the above code we're passing an actual object with just a couple of properties (preload
and create
) instead.
The reason this works is that, internally, the Scene Manager does a lot to determine what's being given to it.
First it looks at the scene
property of the game config. If it's an array it then iterates through it, processing each entry in turn, otherwise it just processes the first entry found. It inspects each item given to it to see what type it is, i.e. a class, an object or a prototype function, and then turns them into full Scene objects, adding them into the Scene Manager.
Ultimately, it doesn't matter which approach you use to define your Scene, once the Scene Manager is done with them they're all treated the same internally anyway. And it's when they get turned into full Scenes that they gain several important additions such as the Scene Settings and Systems, plus the ability to communicate with each other.
Scene Settings
When your Scene is created it extracts any settings you may have defined in its config. If there aren't any, it just uses the defaults. Settings are defined in a configuration object passed to the Scene constructor. Here's an example of setting the Scenes name and physics engine from the config:
class Level1Scene extends Phaser.Scene {
constructor ()
{
super({
key: 'Level1',
physics: {
arcade: {
debug: true,
gravity: { y: 200 }
}
}
});
}
}
The Settings object can also be used to load files. You should really only use this for loading small files as no progress feedback is ever given to the user. Use the following syntax:
class BootScene extends Phaser.Scene {
constructor ()
{
super({
key: 'boot',
files: [
{ type: 'image', key: 'bar', url: 'loaderBar.png' },
{ type: 'image', key: 'bg', url: 'background.png' }
]
});
}
}
The assets are loaded before your Scene Preload method is called.
It's perfect for loading a very small number of graphics, i.e. a background and progress bar image, that your Preloader Scene can then display while loading all the rest of the game assets. Or you could use it to load your internal game config, which could be parsed before loading the rest of the assets.
Scene Systems
When the Scene Manager creates your scene it installs a Scene Systems object into it. This is placed on the property sys
, meaning you should never replace or overwrite the property sys
in your game code, or very bad things will happen :)
The Scene Systems class is the heart of your Scene. It controls all of the Scene plugins, emits events, lets you to modify the Scene (such as sending it to sleep, waking it up, etc) and allows all of the plugins to communicate with the each other.
It will automatically install 7 Core Plugins into your Scenes:
- An Event Emitter
- The 2D Camera Manager
- The Game Object Creator
- The Game Object Factory
- The Scene Plugin
- The Display List
- The Update List
As you can see from the list above, each Scene has its own Display List, factories and Camera system. So when you issue a command like this.add.image
you're interfacing directly with the Scene based Game Object Factory, and adding the resulting image to the Scene level display list.
These plugins are non-optional, as they all rely on each other being present. So every Scene has them. However, you can control which are exposed as properties on the Scene objects via the Injection Map.
There are also 7 Default Plugins. These are installed into your Scenes unless you specify otherwise via the Scene config:
- The 3D Camera Manager
- The Clock
- The Data Manager Plugin
- The Input Plugin
- The Loader Plugin
- The Tween Manager
- The Lights Plugin
The plugins above are all optional. As with the Core Plugins you can control which ones are exposed via properties using the Injection Map.
Configuring Plugins
Remember that the Core Plugins cannot be removed. To remove all of the Default Plugins you simply have to pass an empty plugins array in your scene config:
class BootScene extends Phaser.Scene {
constructor ()
{
super({
key: 'boot',
plugins: []
});
}
}
If you now try and use this.load
in your Scene, it will fail, because the Loader Plugin has been excluded from this Scene.
If you want just the Loader and Tween plugin, but none of the others, you can specify just those:
class BootScene extends Phaser.Scene {
constructor ()
{
super({
key: 'boot',
plugins: [ 'Loader', 'TweenManager' ]
});
}
}
If you're not sure which plugins you will need when you create your Scene you can install them via the Scene Systems. This must be done in an init
method:
class BootScene extends Phaser.Scene {
constructor ()
{
super({
key: 'boot',
plugins: []
});
}
init ()
{
this.sys.install('TweenManager');
}
}
The call to install
tells it to install the Default Plugin TweenManager
, allowing you to add tweens in this Scene.
Scene Injection Map
In Phaser 2 the State Manager would create no less than 19 properties within your State, all linked to global game systems. For example it would take your State and then add the physics
property to it, which mapped to the Physics manager, scale
mapped to the Scale Manager, and so on.
This was a double-edged sword. It made Phaser incredibly easy to use. There was no convoluted access required to a system, you literally just typed it out and there it was. However, you had no choice over the quantity or name of the properties that were injected. If you really wanted to use a local property called world
you couldn't without literally breaking your game. If you accidentally overwrote one of the 19 default properties, it would also break your game. Experienced devs learned to deal with this but we thought there was a better way for Phaser 3.
To that end, we created a Scene Injection Map. This object controls which plugins are mapped to properties within your Scene, and controls exactly what those properties are called.
By default all of the following properties are installed into a Scene:
'anims' = Animation Manager (Global) 'cache' = Cache (Global) 'game' = Phaser.Game instance (Global) 'registry' = Game Data Manager (Global) 'sound' = Sound Manager (Global) 'textures' = Texture Manager (Global)
'add' = Game Object Factory (Local, Core) 'cameras' = 2D Camera Manager (Local, Core) 'children' = Display List (Local, Core) 'events' = Event Emitter (Local, Core) 'make' = Game Object Creator (Local, Core) 'scene' = Scene Manager Plugin (Local, Core)
The following properties are installed into a Scene only if the respective plugin is available:
'cameras3d' = 3D Camera Manager (Local, Optional) 'data' = Scene Data Manager (Local, Optional) 'impact' = Impact Physics (Local, Optional) 'input' = Input Plugin (Local, Optional) 'lights' = Lights Manager Plugin (Local, Optional) 'load' = Loader Plugin (Local, Optional) 'matter' = Matter JS Physics (Local, Optional) 'physics' = Arcade Physics (Local, Optional) 'time' = Time / Clock Plugin (Local, Optional) 'tweens' = Tween Manager (Local, Optional)
If you prefer you can choose not to have any properties added to your Scene at all:
class BootScene extends Phaser.Scene {
constructor ()
{
super({
key: 'boot',
map: {}
});
}
}
Or you can change the name of the property that is injected using the map
object. Here we will replace the English properties with Spanish equivalents:
class BootScene extends Phaser.Scene {
constructor ()
{
super({
key: 'boot',
map: {
add: 'agregar',
anims: 'animaciones',
cameras: 'camaras',
events: 'eventos',
load: 'cargar',
sound: 'sonido'
}
});
}
preload ()
{
this.cargar.image('face', 'assets/bw-face.png');
}
create ()
{
this.agregar.image(400, 300, 'face');
}
}
If your IDE supports it, you can even use UTF8, such as this Simplified Chinese version:
class BootScene extends Phaser.Scene {
constructor ()
{
super({
key: 'boot',
map: {
add: '加',
load: '加载'
}
});
}
preload ()
{
this.加载.image('face', 'assets/pics/bw-face.png');
}
create ()
{
this.加.image(400, 300, 'face');
}
}
Scene Callbacks
The scene callback methods are init()
, preload()
, create()
, and update()
. The archetypal callback pattern is
// Configure this scene cycle
function init(data) {}
// Queue assets for downloading
function preload() {}
// Create game objects with loaded assets
function create(data) {}
// Work on game objects at each game step
function update(time, delta) {}
init()
is less often used.
You can create game objects in any method, but if you've queued assets in preload()
then you won't be able to use those assets before create()
.
If you don't use preload()
then the pattern is simply:
// Create game objects
function create(data) {}
// Work on game objects at each game step
function update(time, delta) {}
Either init()
or create()
can be used.
Within the scene callbacks, this is the scene. If you don't much like using this
, you can assign it to a variable:
let scene;
function init() {
if (scene) throw new Error("Only one scene can use the `scene` variable");
scene = this;
scene.events.once("destroy", () => {
scene = null;
});
scene.events.once("shutdown", () => {
scene = null;
});
}
Or you can destructure it within a callback:
function create() {
const { add, cameras, events } = this;
add.sprite(/*…*/);
}
Scene creation
Scenes can be created from a class or a config object. They're added to the game through the Scene Manager. This can be done through the game config, the add()
methods, or (rarely) load.scene().
- If a config object is given, a new
Phaser.Scene
is instantiated and some of the config values are copied onto it. - If a class is given, it's instantiated.
- The scene is booted.
- The scene is started, if you indicated so.
Every scene in the Scene Manager's list is an object (instance) that's been booted, and lasts until it's removed (destroyed).
Scene creation basics
Using an ES6 class
class MyScene extends Phaser.Scene {
constructor(config) {
super(config);
}
init(data) {}
preload() {}
create(data) {}
update(time, delta) {}
}
data
: Parameters passed from adding new scene, or starting scene
Using a Class
var MyScene = new Phaser.Class({
Extends: Phaser.Scene,
initialize: function MyScene(config) {
Phaser.Scene.call(this, config);
},
init: function (data) {},
preload: function () {},
create: function (data) {},
update: function (time, delta) {},
});
data
: Parameters passed from adding new scene, or starting scene
var MyGame = {};
MyGame.Boot = function () {};
MyGame.Boot.prototype.constructor = MyGame.Boot;
MyGame.Boot.prototype = {
init: function (data) {},
preload: function () {},
create: function (data) {},
update: function (time, delta) {},
};
data
: Parameters passed from adding new scene, or starting scene
Overriding default callbacks
var demo = new Phaser.Scene("Demo");
demo.init = function (data) {};
demo.preload = function () {};
demo.create = function (data) {};
demo.update = function (time, delta) {};
data
: Parameters passed from adding new scene, or starting scene
Creating a single-scene game
It's simplest to use the game config. When only one scene is given, it is started automatically.
A scene config in the game config
const sceneConfig = {
create: function () {
/*…*/
},
};
new Phaser.Game({
scene: sceneConfig,
});
A scene class in the game config
class Scene extends Phaser.Scene {
create() {
/*…*/
}
}
new Phaser.Game({
scene: Scene,
});
A scene instance in the game config
class Scene extends Phaser.Scene {
create() {
/*…*/
}
}
new Phaser.Game({
scene: new Scene(),
});
You may also use scene.add()
, although there's no great advantage in this case. autoStart
(the third argument) means start the scene at the time that's added.
Add a scene from a config object
const sceneConfig = {
create: function () {
/*…*/
},
};
new Phaser.Game({
callbacks: {
preBoot: (game) => {
game.scene.add("default", sceneConfig, true);
},
},
});
Add a scene from a scene class
class Scene extends Phaser.Scene {
create() {
/*…*/
}
}
new Phaser.Game({
callbacks: {
preBoot: (game) => {
game.scene.add("default", Scene, true);
},
},
});
Add a scene from a scene instance
class Scene extends Phaser.Scene {
create() {
/*…*/
}
}
new Phaser.Game({
callbacks: {
preBoot: (game) => {
game.scene.add("default", new Scene(), true);
},
},
});
Beware that with scene.add()
, it's possible provide conflicting scene keys:
// OOPS
game.scene.add("yin", new Scene("yang"), true);
In this case the scene is instantiated with the key 'yang' and then the Scene Manager changes the key to 'yin'.
Creating a multi-scene game
In a multi-scene game, each scene needs a unique key.
Again, it's easiest to add scenes in the game config. The first scene plus any additional scenes with { active: true }
are started automatically.
Multiple scene configs in the game config
const bootSceneConfig = { key: 'boot', /*…*/ };
const playSceneConfig = { key: 'play', /*…*/ };
const uiSceneConfig = { key: 'ui', active: true };
new Phaser.Game({
// 'boot' and 'ui' will be started
scene: [ bootSceneConfig, playSceneConfig, uiSceneConfig ]
};
Multiple scene instances in the game config
// Scene classes:
class BootScene {
/*…*/
}
class PlayScene {
/*…*/
}
class UIScene {
/*…*/
}
new Phaser.Game({
// 'boot' and 'ui' will be started
scene: [
new BootScene("boot"),
new PlayScene("play"),
new UIScene({ key: "ui", active: true }),
],
});
You can configure scenes in their constructors instead if you like.
Multiple scenes from a base config
const levelSceneConfig = { preload, create, update };
const level1SceneConfig = { ...levelSceneConfig, key: 'level1' };
const level2SceneConfig = { ...levelSceneConfig, key: 'level2' };
new Phaser.Game({
scene: [ level1SceneConfig, level2SceneConfig ]
};
Multiple scenes from one class
class LevelScene {/*…*/};
new Phaser.Game({
scene: [ new LevelScene('level1'), new LevelScene('level2') ]
};
A 2-scene game
class Boot extends Phaser.Scene {
// Load assets
preload() {}
// Start Play scene
create() {}
}
class Play extends Phaser.Scene {
// Create game objects with loaded assets
create() {}
// Update the scene
update() {}
}
new Phaser.Game({
scene: [
// Only 'boot' will start.
new Boot("boot"),
new Play("play"),
],
});
Listing scenes after game boot
new Phaser.Game({
scene: [
/* etc. */
],
callbacks: {
postBoot: function (game) {
game.scene.dump();
// Look for output in console.
},
},
});
Scene Configuration Object
Here is an example of a scene configuration object:
var config = {
key: "",
// active: false,
// visible: true,
// pack: false,
// cameras: null,
// map: {},
// physics: {},
// loader: {},
// plugins: false,
// input: {}
};
-
key
: The unique key of this Scene. Must be unique within the entire Game instance. -
active
: Does the Scene start as active or not? An active Scene updates each step. -
visible
: Does the Scene start as visible or not? A visible Scene renders each step. -
pack
: An optional Loader Packfile to be loaded before the Scene begins. -
cameras
: An optional Camera configuration object.{ name: '', x: 0, y: 0, width: scene.sys.scale.width, height: scene.sys.scale.height, zoom: 1, rotation: 0, scrollX: 0, scrollY: 0, roundPixels: false, visible: true, backgroundColor: false, bounds: null, // {x, y, width, height} }
map
: Overwrites the default injection map for a scene.
-
physics
: The physics configuration object for the Scene.{ default: 'arcade', // 'arcade', or 'matter' arcade: {...}, matter: {...} }
arcade
: Arcade Physics configuration.matter
: Matter Physics configuration.
-
loader
: The loader configuration object for the Scene.{ baseURL: '', path: '', enableParallel: true, maxParallelDownloads: 4, crossOrigin: undefined, responseType: '', async: true, user: '', password: '', timeout: 0 }
-
plugins
: The plugin configuration object for the Scene.{ global: [ //{key, plugin, start} ], scene: [ // ... ] }
-
input
: The input configuration object for the Scene.
Scene life cycle
A scene is booted once and can be started any number of times (or never). It lasts until it's removed (destroyed).
A scene that hasn't been added to the manager has status PENDING
.
A scene that has been added to the manager but never started has status INIT
. (This means "booted" and is unrelated to the scene init()
method.)
When a scene starts it goes through statuses START
, LOADING
(if assets were queued during preload()
), CREATING
, and RUNNING
. Its methods init()
, preload()
, and create()
are called also, if they exist. update()
is called at each game step while the scene is in the RUNNING
state.
A RUNNING
scene can change to PAUSED
and back by pause/resume or to SLEEPING
and back by sleep/wake.
A scene that has been stopped has status SHUTDOWN
. It can be started again.
A scene that has been removed/destroyed has status DESTROYED
. It can't be used again.
A scene is active only during START
, LOADING
, CREATING
, and RUNNING
; and visible only during START
, LOADING
, CREATING
, RUNNING
, and PAUSED
.
Flowchart: Scene life cycle
Run
: Update and renderPause
: Render but no updateSleep
: No update, no renderStop
: Shutdown
Changing scenes
There are a lot of ways, because it depends on what you want to do, and Phaser lets you run multiple scenes at once.
Scene operations
The basic operation pairs are pause–resume, sleep–wake, and shutdown–start.
Usually, with a typical gameplay scene, you will enter the scene with start and exit with shutdown. Gameplay starts over each time.
But to move to an intermission scene and back, you might exit the gameplay scene with sleep and then reenter with wake. It's hidden in the meantime, but then the player returns to the same state they left.
And to move to a modal scene and back, you might exit the gameplay scene with pause and then reenter with resume. In this case it's still visible but suspended.
With scenes like menu or title screens that you expect the player to revisit, you have a choice of sleep–wake or shutdown–start. The sleep–wake pattern may be more manageable. It's easier to reason about scenes that start only once.
Scene control methods
The simple methods
launch()
andstop()
do start and shutdown.sleep()
andwake()
do sleep and wake.pause()
andresume()
do pause and resume.
The conditional methods
run()
may start, wake, or resume a sceneswitch()
may start or wake a scene
The "double" methods
These affect both the target scene and the calling scene. They're convenient for moving through scenes one at a time.
start()
stops the calling scene.switch()
sleeps the calling scene.
start()
start('target')
starts the target scene and stops the calling scene. It's equivalent to stop().launch('target')
.
If you want to start a second scene without stopping anything, use launch('target')
instead.
// In scene A: stop A, start B
this.scene.start("B");
// In scene B: stop B, start C
this.scene.start("C");
// In scene C: stop C, start A again
this.scene.start("A");
switch()
switch('target')
starts or wakes the target scene and sleeps the calling scene.
// In scene A: sleep A, start B
this.scene.switch("B");
// In scene B: sleep B, start C
this.scene.switch("C");
// In scene C: sleep C, *wake* A
this.scene.switch("A");
Here each scene is started only once, and never shut down.
A switch()
equivalent is
this.scene.sleep();
if (this.scene.isSleeping('target') {
this.scene.wake('target');
} else {
this.scene.launch('target');
}
switch()
restarts a paused scene, never resumes it — cf. run()
.
launch()
launch()
starts or restarts the target scene. It never resumes or wakes — cf. run()
.
run()
run()
resumes the target scene if paused; wakes it if sleeping; restarts it if running; and starts it otherwise.
Problems restarting a scene
Some common causes:
- Your own state variables haven't been reset
- You're working on an invalid object from the previous scene cycle, e.g., a game object or camera
- Often, a destroyed game object's method is still registered to an event emitter
Example: bad state after restart
let gameOver = false;
function update() {
if (gameOver) {
// GAME OVER :(
}
}
After restarting the scene it's GAME OVER immediately, because the gameOver
variable hasn't been reset. Rewrite as
let gameOver;
function init() {
gameOver = false;
}
function update() {
if (gameOver) {
// GAME OVER :(
}
}
In a scene class, you might introduce the same problem in the scene constructor:
class Scene extends Phaser.Scene {
constructor() {
super();
this.gameOver = false;
}
}
Rewrite it similarly:
class Scene extends Phaser.Scene {
constructor() {
super();
this.gameOver;
}
init() {
this.gameOver = false;
}
}
Example: invalid game objects after restart
const sprites = [];
function create() {
sprites.push(this.physics.add.sprite(/*…*/));
}
function update(time) {
for (const sprite of sprites) {
sprite.setVelocity(1, 1);
// > TypeError: undefined is not an object (evaluating 'this.body.setVelocityX')
}
}
Here you get an error after restarting the scene. The destroyed sprites from the last cycle are still in the sprites
array. Rewrite as
function create() {
// …
this.events.once("shutdown", () => {
sprites.length = 0;
});
}
Removing a scene
If you never use a scene again, you can remove it:
this.scene.destroy();
Replacing a scene with the same key
You can't reuse the key until the original scene is removed.
A scene removing itself
// After destruction, `this.scene` and `this.sys.scenePlugin` are unusable.
// So we need to use the manager directly.
const { manager } = this.scene;
this.events.once('destroy', () => {
manager.add('key', newScene);
}
this.scene.remove();
A second scene removing the first
sceneToRemove.events.once('destroy', () => {
this.scene.add('key', newScene);
}
this.scene.remove(sceneToRemove);
An alternative
Instead of juggling scene keys, you can use dynamic keys.
this.registry.set("levelSceneKey", "level1");
this.scene.remove(this.registry.get("levelSceneKey"));
this.registry.set("levelSceneKey", "level2");
this.scene.add(this.registry.get("levelSceneKey"), Level, true);
Scene systems and plugins
Scenes have their own systems, core plugins that are always installed and default plugins that are installed unless disabled. Any extra scene plugins you've added in the game config with { start: true }
or any { mapping: … }
are also default plugins.
If you've added a scene plugin to the game config without those, the plugin isn't installed by default, and you can install it in a specific scene by adding its key to the plugins
array:
new Phaser.Scene({
plugins: [
...Phaser.Plugins.DefaultPlugins.DefaultScene,
"SomeOtherPluginKey",
],
});
You can also remove default plugins this way:
new Phaser.Scene({
// "DataManagerPlugin", "Loader", and "LightsPlugin" are omitted.
plugins: ["Clock", "InputPlugin", "TweenManager"],
});
Physics plugins are different. They are installed in a scene if the game config's physics.default
is 'arcade'
or 'matter'
or if the scene settings config includes physics.arcade
or physics.matter
config objects (even if empty).
Table: Systems and plugins
The table below is a list of systems and plugins (bolded in the table below) as well as references to some global systems.
These can be mapped onto different property names if you like.
Scene | Systems | Notes |
---|---|---|
add | sys.add | Core plugin |
anims | sys.anims | |
cache | sys.cache | |
cameras | sys.cameras | Core plugin |
children | sys.displayList | Core plugin |
data | sys.data | Default plugin |
events | sys.events | Core plugin. Different from |
sys.facebook | ||
game | sys.game | |
input | sys.input | Default plugin |
lights | sys.lights | Default plugin |
load | sys.load | Default plugin |
make | sys.make | Core plugin |
matter | sys.matterPhysics | Optional plugin |
physics | sys.arcadePhysics | Optional plugin |
plugins | sys.plugins | |
registry | sys.registry | |
renderer | sys.renderer | |
scale | sys.scale | |
scene | sys.scenePlugin | Core plugin |
sound | sys.sound | |
textures | sys.textures | |
time | sys.time | Default plugin |
tweens | sys.tweens | Default plugin |
Rendering
Scenes render from first to last (bottom to top). You can rearrange them with Scene Manager methods like bringToTop()
, or sendToBack()
, but this is unusual, except for some visual effects. Don't use these methods to "fix" visibility problems when changing scenes — you should probably be using sleep/wake or start/stop instead. Remember you can add all your scene definitions to the game config and so establish the rendering order there, even for scenes that aren't started immediately.
You can toggle a scene's visibility with scene.setVisible()
. This doesn't change the scene's status.
Scene Events
-
Start (Before
scene.init()
)scene.events.on("start", function () {});
-
Ready (After
start
)scene.events.on("ready", function () {});
-
Every tick
-
Preupdate
scene.events.on("preupdate", function (time, delta) {});
-
Update
scene.events.on("update", function (time, delta) {});
-
Postupdate
scene.events.on("postupdate", function (time, delta) {});
-
Render
scene.events.on("render", function () {});
-
-
State changed
-
Pause (from
scene.scene.pause()
)scene.events.on("pause", function () {});
-
Resume (from
scene.scene.resume()
)scene.events.on("resume", function () {});
-
Sleep (from
scene.scene.sleep()
)scene.events.on("sleep", function () {});
-
Wake (from
scene.scene.wake()
)scene.events.on("wake", function () {});
-
Stop/shutdown (from
scene.scene.stop()
)scene.events.on("shutdown", function () {});
- Free-up any resources that may be in use by this scene
-
-
Destroy (from
scene.scene.remove()
)scene.events.on("destroy", function () {});
-
Resize
scene.events.on("resize", function () {});
-
Boot
scene.events.on("boot", function () {});
-
Game object added to scene
-
Add
scene.events.on("addedtoscene", function (gameObject, scene) {});
-
Remove
scene.events.on("removedfromscene", function (gameObject, scene) {});
-
Author Credits
Content on this page includes work by: