- name
- events-system
- description
- Use this skill when working with the Phaser 4 event system. Covers EventEmitter, scene events, game events, custom events, and event-driven communication. Triggers on: events, on, emit, EventEmitter, scene events, listeners.
# Events System
> Phaser uses the EventEmitter pattern (via eventemitter3) throughout the entire framework. Every major system -- Game, Scene, Input, Loader, Cameras, Sound, Tweens, Physics, Textures, Animations -- is an EventEmitter or contains one. Events use lowercase string keys. Phaser provides named constants for all built-in events to avoid typos and enable IDE autocomplete.
**Key source paths:** `src/events/EventEmitter.js`, `src/scene/events/`, `src/core/events/`, `src/input/events/`, `src/loader/events/`, `src/animations/events/`, `src/cameras/2d/events/`, `src/sound/events/`, `src/tweens/events/`, `src/physics/arcade/events/`, `src/textures/events/`, `src/gameobjects/events/`, `src/time/events/`
**Related skills:** ../scenes/SKILL.md, ../input-keyboard-mouse-touch/SKILL.md
## Quick Start
```js
// on — listen for an event (persists until removed)
this.input.on('pointerdown', (pointer) => {
console.log('clicked at', pointer.x, pointer.y);
});
// once — listen for an event, auto-removes after first fire
this.events.once('shutdown', () => {
console.log('scene shutting down');
});
// off — remove a specific listener (must pass same function reference)
const handler = (pointer) => { /* ... */ };
this.input.on('pointerdown', handler);
this.input.off('pointerdown', handler);
// emit — fire a custom event with arguments
this.events.emit('player-died', this.player, this.score);
// removeAllListeners — remove all listeners for an event (or all events)
this.events.removeAllListeners('player-died');
this.events.removeAllListeners(); // all events
```
### Using Named Constants (Preferred)
```js
// Always prefer constants over raw strings to prevent typos
this.events.on(Phaser.Scenes.Events.UPDATE, (time, delta) => {
// runs every frame
});
this.input.on(Phaser.Input.Events.POINTER_DOWN, (pointer) => {
// pointer pressed
});
this.game.events.on(Phaser.Core.Events.BLUR, () => {
// browser tab lost focus
});
```
## Core Concepts
### EventEmitter Base Class
`Phaser.Events.EventEmitter` extends eventemitter3. It adds `shutdown()` and `destroy()` methods that both call `removeAllListeners()`.
**Full API (inherited from eventemitter3):**
| Method | Description |
|---|---|
| `on(event, fn, context?)` | Add persistent listener. Returns `this` for chaining |
| `addListener(event, fn, context?)` | Alias for `on` |
| `once(event, fn, context?)` | Add one-time listener; auto-removed after first fire |
| `off(event, fn?, context?, once?)` | Remove listener(s). Must pass same `fn` reference to remove specific listener |
| `removeListener(event, fn?, context?, once?)` | Alias for `off` |
| `removeAllListeners(event?)` | Remove all listeners for event, or all events if no arg |
| `emit(event, ...args)` | Fire event. Returns `true` if it had listeners |
| `listeners(event)` | Return array of listener functions for an event |
| `listenerCount(event)` | Return number of listeners for an event |
| `eventNames()` | Return array of event names that have listeners |
| `shutdown()` | Calls `removeAllListeners()` |
| `destroy()` | Calls `removeAllListeners()` |
### Event Strings vs Constants
Every built-in event is a lowercase string exported as a constant. The constant name maps predictably to the string:
```js
Phaser.Scenes.Events.UPDATE // 'update'
Phaser.Scenes.Events.PRE_UPDATE // 'preupdate'
Phaser.Scenes.Events.SHUTDOWN // 'shutdown'
Phaser.Core.Events.BOOT // 'boot'
Phaser.Input.Events.POINTER_DOWN // 'pointerdown'
```
Some events use a key-suffix pattern for per-key listening:
```js
// Loader: listen for a specific file completing
this.load.on(Phaser.Loader.Events.FILE_KEY_COMPLETE + 'image-logo', (key, type, data) => {});
// String value: 'filecomplete-image-logo'
// Animations: listen for a specific animation completing on a sprite
sprite.on(Phaser.Animations.Events.ANIMATION_COMPLETE_KEY + 'walk', () => {});
// String value: 'animationcomplete-walk'
// Textures: listen for a specific texture being added
this.textures.on(Phaser.Textures.Events.ADD_KEY + 'myTexture', () => {});
// String value: 'addtexture-myTexture'
```
### Context (Third Argument)
The third argument to `on`/`once` sets `this` inside the callback. Defaults to the emitter.
```js
// 'this' inside handler refers to the scene
this.input.on('pointerdown', function (pointer) {
this.cameras.main.shake(100); // 'this' = scene
}, this);
// Arrow functions ignore the context argument (they capture lexical 'this')
this.input.on('pointerdown', (pointer) => {
this.cameras.main.shake(100); // 'this' = enclosing scope (scene in create)
});
```
## Common Patterns
### Scene Lifecycle Events
Frame loop order: `preupdate` -> `update` -> `Scene.update()` -> `postupdate` -> `prerender` -> `render`
```js
create() {
this.events.on(Phaser.Scenes.Events.UPDATE, this.onUpdate, this);
// CRITICAL: always clean up on shutdown to prevent leaks on scene restart
this.events.on(Phaser.Scenes.Events.SHUTDOWN, () => {
this.events.off(Phaser.Scenes.Events.UPDATE, this.onUpdate, this);
this.input.off('pointerdown', this.onPointerDown, this);
});
}
```
### Game-Level Events
```js
// game.events fires on the Game instance, shared across all scenes
// Access from a scene via this.game.events
this.game.events.on(Phaser.Core.Events.BLUR, this.handleBlur, this);
this.game.events.on(Phaser.Core.Events.VISIBLE, this.handleVisible, this);
```
### Inter-Scene Communication
```js
// METHOD 1: game.events — a global event bus accessible from all scenes
// Scene A emits:
this.game.events.emit('score-changed', this.score);
// Scene B listens:
this.game.events.on('score-changed', (score) => { this.scoreText.setText(score); });
// METHOD 2: this.registry — a shared DataManager across all scenes
// The registry is a Phaser.Data.DataManager on the Game instance.
// Scene A sets data:
this.registry.set('score', 100);
// Scene B listens for changes:
this.registry.events.on('changedata-score', (parent, value, previousValue) => {
this.scoreText.setText(value);
});
// METHOD 3: Direct scene access via ScenePlugin
this.scene.get('UIScene').events.emit('update-health', hp);
```
### Custom Events
```js
// Emit custom events with arbitrary data arguments
this.events.emit('player-died', this.player, { lives: this.lives });
this.events.on('player-died', (player, data) => {
console.log('Lives remaining:', data.lives);
});
```
## All Event Namespaces Reference
### Scene Events (`Phaser.Scenes.Events`)
Emitter: `this.events` (the Scene's Systems EventEmitter)
| Constant | String | When |
|---|---|---|
| `BOOT` | `'boot'` | Scene Systems boot (for plugins) |
| `READY` | `'ready'` | Scene Systems fully ready |
| `START` | `'start'` | Scene starts running |
| `CREATE` | `'create'` | After Scene.create() completes |
| `PRE_UPDATE` | `'preupdate'` | Before update each frame |
| `UPDATE` | `'update'` | Main update each frame |
| `POST_UPDATE` | `'postupdate'` | After update each frame |
| `PRE_RENDER` | `'prerender'` | Before render each frame |
| `RENDER` | `'render'` | During render each frame |
| `PAUSE` | `'pause'` | Scene paused |
| `RESUME` | `'resume'` | Scene resumed from pause |
| `SLEEP` | `'sleep'` | Scene put to sleep |
| `WAKE` | `'wake'` | Scene woken from sleep |
| `SHUTDOWN` | `'shutdown'` | Scene shutting down (may restart) |
| `DESTROY` | `'destroy'` | Scene permanently destroyed |
| `ADDED_TO_SCENE` | `'addedtoscene'` | GameObject added to scene |
| `REMOVED_FROM_SCENE` | `'removedfromscene'` | GameObject removed from scene |
| `TRANSITION_INIT` | `'transitioninit'` | Transition initialized (target scene) |
| `TRANSITION_START` | `'transitionstart'` | Transition started (target scene) |
| `TRANSITION_OUT` | `'transitionout'` | Transition out (source scene) |
| `TRANSITION_COMPLETE` | `'transitioncomplete'` | Transition finished |
| `TRANSITION_WAKE` | `'transitionwake'` | Transition wakes target scene |
### Game Events (`Phaser.Core.Events`)
Emitter: `this.game.events` or `game.events`
| Constant | String | When |
|---|---|---|
| `BOOT` | `'boot'` | Game instance finished booting |
| `READY` | `'ready'` | Game ready to start running |
| `SYSTEM_READY` | `'systemready'` | All global systems ready |
| `PRE_STEP` | `'prestep'` | Before game loop step |
| `STEP` | `'step'` | Main game loop step |
| `POST_STEP` | `'poststep'` | After game loop step |
| `PRE_RENDER` | `'prerender'` | Before rendering all scenes |
| `POST_RENDER` | `'postrender'` | After rendering all scenes |
| `PAUSE` | `'pause'` | Game paused |
| `RESUME` | `'resume'` | Game resumed |
| `BLUR` | `'blur'` | Browser tab lost focus |
| `FOCUS` | `'focus'` | Browser tab gained focus |
| `HIDDEN` | `'hidden'` | Page Visibility API: hidden |
| `VISIBLE` | `'visible'` | Page Visibility API: visible |
| `CONTEXT_LOST` | `'contextlost'` | WebGL context lost |
| `DESTROY` | `'destroy'` | Game being destroyed |
### Input Events (`Phaser.Input.Events`)
Emitter: `this.input` (scene-level) or individual GameObjects. Events exist at three levels: scene-level (`this.input`), scene-level with gameobject prefix, and directly on interactive GameObjects. See ../input-keyboard-mouse-touch/SKILL.md for full usage.
**Scene-level pointer events (on `this.input`):**
`POINTER_DOWN` `'pointerdown'` | `POINTER_UP` `'pointerup'` | `POINTER_MOVE` `'pointermove'` | `POINTER_OVER` `'pointerover'` | `POINTER_OUT` `'pointerout'` | `POINTER_WHEEL` `'wheel'` | `POINTER_DOWN_OUTSIDE` `'pointerdownoutside'` | `POINTER_UP_OUTSIDE` `'pointerupoutside'`
**Scene-level gameobject events (on `this.input`):**
`GAMEOBJECT_DOWN` `'gameobjectdown'` | `GAMEOBJECT_UP` `'gameobjectup'` | `GAMEOBJECT_MOVE` `'gameobjectmove'` | `GAMEOBJECT_OVER` `'gameobjectover'` | `GAMEOBJECT_OUT` `'gameobjectout'` | `GAMEOBJECT_WHEEL` `'gameobjectwheel'`
**Per-GameObject events (emitted on the GameObject itself, requires `setInteractive()`):**
`GAMEOBJECT_POINTER_DOWN` `'pointerdown'` | `GAMEOBJECT_POINTER_UP` `'pointerup'` | `GAMEOBJECT_POINTER_MOVE` `'pointermove'` | `GAMEOBJECT_POINTER_OVER` `'pointerover'` | `GAMEOBJECT_POINTER_OUT` `'pointerout'` | `GAMEOBJECT_POINTER_WHEEL` `'wheel'`
**Drag events (on `this.input` and on GameObjects with same string):**
GitHub에서 보기