Phaser Basics

Updated at 2022-06-20 07:44

Phaser is a free, open source game development framework. You use TypeScript and other web technologies for the development. The result runs on browsers by default but you can use third party tools to compile the game to run as a native application.

GameObject is the main low-level building block of a Phaser game. Everything visual or intractable, from images and shapes to particle effects and videos, are game objects.

Group is a collection of game objects in a scene.

  • Nonexclusive membership
  • Can disable or enable all children in one-go.
  • Can hide or show all children in one-go.
  • Can remove all children in one-go.
  • Allows reusing disabled game objects to conserve resources with e.g. get().

Container is a collection of game objects in a scene.

  • Exclusive membership
  • Children move relative to the container.
  • Children are rendered when the container is rendered.
  • Containers can be nested.
  • Containers can be expensive for performance.

Scene is the main high-level building block of a Phaser game. Scene is a collection of game objects and surrounding logic. You can have as many running scenes as you want. You usually split your game to e.g. the gameplay scene and the user interface overlay scene.

Each scene has 5 core methods that can define. I'll also mention the main use-case for each method.

  • constructor is ran once when the scene is defined, useful for initializing variables.
  • init: is ran each time the scene starts, allows e.g switching to another Scene before any loading happens or receiving startup information from other scenes.
  • preload: is ran after init is used to load assets like images with this.load.
  • create: is ran after preload and used to create game objects with this.add as assets are now available.
  • update: is ran once on each game loop if scene is active.

You can use a Scene constructor files property which will be loaded before preload. These constructor files are good for either simple graphics required for a loading screen or small configuration files required to determine which assets to download in preload.

public constructor() {
  super({key: SceneName.Boot, files: {
    {type: 'image', key: 'background', url: 'assets/background.png'}

Note that init, preload and create can be called again sometime later. For example, if you do stop() and sometime later start() on the scene.

The first listed scene in the game config is started automatically.

new Phaser.Game({
  scenes: [Scene1, Scene2, Scene3],

You can add scenes with this.scene.add if it was not included in the game config. But they are not automatically started. You start scenes with start (that also stops the current scene) or launch (which doesn't stop the current scene.)

You can destroy scenes with this.scene.remove. Or simply pause them with pause() (doesn't call update each loop) or make them inactive with stop() (the next start will rerun the initialization methods).

Scenes share a global Cache. All scenes use the same, potentially cached, files.

Scenes share a global Registry which is a DataManager. This global DataManager can be used for cross-scene communication.

Each scene has core plugins that can't be removed:

  • An Event Emitter
  • The 2D Camera Manager
  • The Game Object Creator
  • The Game Object Factory
  • The Scene Plugin
  • The Display List
  • The Update List

Each scene has some plugins that can optionally be removed in the constructor:

  • The 3D Camera Manager
  • The Clock
  • The Data Manager Plugin
  • The Input Plugin
  • The Loader Plugin
  • The Tween Manager
  • The Lights Plugin

Scenes communicate by:

  1. direct method calls e.g. this.scene.get('sceneName').doIt()
  2. events through the default Scene EventEmitter at
  3. events through a custom EventEmitter instance, to separate from system events
  4. values and events through this.registry, which is intended for scene comms
  5. any other JavaScript-based paradigm like Redux, just pass comm objects to scenes

The right communication approach depends on your game but... Using this.registry is a good practice, while custom paradigms are only beneficial for games with complex game state and logic. Main benefit of custom paradigms is more thorough testing (no need to rely on Phaser stuff) and forcing separation of logic and visuals.

# the registry approach:
this.registry.set('score', 0);
this.registry.on('changedata' (_parent, key, data) => void, this)

# redux-in-a-nutshell:
# * Always separate 1) canonical state 2) view state and 3) edit state
# * Input triggers an action.
# * Action updates game state.
# * The action optionally triggers separate action for UI changes.
const state = getGameState()
const store = createGameStore(state)
const scene = new BootScene('BootScene', {store})