0
# Scene Management
1
2
Scenes are self-contained game states that manage their own display lists, update loops, input handling, and systems. They form the structural backbone of Phaser games, representing different screens like menus, gameplay, pause screens, and game over states.
3
4
## Scene Basics
5
6
### Scene Class
7
The base `Phaser.Scene` class provides the foundation for all game scenes.
8
9
```javascript { .api }
10
class GameScene extends Phaser.Scene {
11
constructor() {
12
super({ key: 'GameScene' });
13
}
14
15
init(data) {
16
// Initialize scene with passed data
17
this.score = data.score || 0;
18
this.level = data.level || 1;
19
}
20
21
preload() {
22
// Load assets for this scene
23
this.load.image('player', 'assets/player.png');
24
this.load.audio('bgm', 'assets/music.mp3');
25
}
26
27
create(data) {
28
// Create game objects and set up scene
29
this.player = this.add.sprite(400, 300, 'player');
30
this.music = this.sound.add('bgm');
31
this.music.play({ loop: true });
32
}
33
34
update(time, delta) {
35
// Game logic updated every frame
36
if (this.input.keyboard.addKey('SPACE').isDown) {
37
this.player.y -= 200 * (delta / 1000);
38
}
39
}
40
}
41
```
42
43
### Scene Configuration
44
Scenes can be configured with various options:
45
46
```javascript { .api }
47
class MenuScene extends Phaser.Scene {
48
constructor() {
49
super({
50
key: 'MenuScene',
51
active: true, // Start active
52
visible: true, // Start visible
53
pack: { // Preload pack
54
files: [
55
{ type: 'image', key: 'logo', url: 'assets/logo.png' }
56
]
57
},
58
cameras: { // Camera configuration
59
name: 'menuCam',
60
x: 0,
61
y: 0,
62
width: 800,
63
height: 600
64
},
65
map: { // Tilemap auto-load
66
key: 'menu_map',
67
tileWidth: 32,
68
tileHeight: 32
69
},
70
physics: { // Physics configuration
71
default: 'arcade',
72
arcade: {
73
gravity: { y: 300 }
74
}
75
}
76
});
77
}
78
}
79
```
80
81
## Scene Lifecycle
82
83
### Lifecycle Methods
84
Scenes have a defined lifecycle with specific callback methods:
85
86
```javascript { .api }
87
class LifecycleScene extends Phaser.Scene {
88
init(data) {
89
// Called first when scene starts
90
// Use for variable initialization
91
console.log('Scene initializing with data:', data);
92
this.playerName = data.playerName || 'Player';
93
}
94
95
preload() {
96
// Called after init, used for asset loading
97
this.load.image('background', 'assets/bg.jpg');
98
this.load.spritesheet('character', 'assets/char.png', {
99
frameWidth: 32,
100
frameHeight: 48
101
});
102
103
// Show loading progress
104
this.load.on('progress', (percent) => {
105
console.log('Loading:', Math.round(percent * 100) + '%');
106
});
107
}
108
109
create(data) {
110
// Called after preload completes
111
// Create game objects and set up scene
112
this.add.image(400, 300, 'background');
113
this.character = this.add.sprite(100, 400, 'character');
114
115
// Set up input
116
this.cursors = this.input.keyboard.createCursorKeys();
117
118
// Scene is now ready for interaction
119
console.log('Scene ready for', this.playerName);
120
}
121
122
update(time, delta) {
123
// Called every frame while scene is active
124
if (this.cursors.left.isDown) {
125
this.character.x -= 150 * (delta / 1000);
126
}
127
if (this.cursors.right.isDown) {
128
this.character.x += 150 * (delta / 1000);
129
}
130
}
131
}
132
```
133
134
### Scene Events
135
Scenes emit events during their lifecycle:
136
137
```javascript { .api }
138
class EventScene extends Phaser.Scene {
139
create() {
140
// Listen to scene events
141
this.events.on('create', () => {
142
console.log('Scene created');
143
});
144
145
this.events.on('wake', (sys, data) => {
146
console.log('Scene woken up with data:', data);
147
});
148
149
this.events.on('sleep', () => {
150
console.log('Scene going to sleep');
151
});
152
153
this.events.on('pause', () => {
154
console.log('Scene paused');
155
});
156
157
this.events.on('resume', () => {
158
console.log('Scene resumed');
159
});
160
161
this.events.on('shutdown', () => {
162
console.log('Scene shutting down');
163
});
164
165
this.events.on('destroy', () => {
166
console.log('Scene destroyed');
167
});
168
}
169
}
170
```
171
172
## Scene Management
173
174
### Scene Plugin
175
The Scene Plugin (`this.scene`) provides methods for managing scenes:
176
177
```javascript { .api }
178
class GameScene extends Phaser.Scene {
179
create() {
180
// Start another scene
181
this.scene.start('NextScene', { score: this.score });
182
183
// Launch scene in parallel
184
this.scene.launch('UIScene');
185
this.scene.launch('BackgroundScene');
186
187
// Switch to scene (stops current, starts new)
188
this.scene.switch('MenuScene');
189
190
// Pause/Resume scenes
191
this.scene.pause('GameScene');
192
this.scene.resume('GameScene');
193
194
// Sleep/Wake scenes (like pause but stops updates)
195
this.scene.sleep('GameScene');
196
this.scene.wake('GameScene', { newData: 'value' });
197
198
// Stop scene
199
this.scene.stop('GameScene');
200
201
// Restart current scene
202
this.scene.restart({ resetScore: true });
203
204
// Set scene visibility
205
this.scene.setVisible(false, 'BackgroundScene');
206
207
// Move scenes up/down in render order
208
this.scene.moveUp('UIScene');
209
this.scene.moveDown('BackgroundScene');
210
this.scene.bringToTop('UIScene');
211
this.scene.sendToBack('BackgroundScene');
212
}
213
}
214
```
215
216
### Scene Manager
217
Access the global scene manager through `this.scene.manager`:
218
219
```javascript { .api }
220
class ManagerScene extends Phaser.Scene {
221
create() {
222
const manager = this.scene.manager;
223
224
// Get scene references
225
const gameScene = manager.getScene('GameScene');
226
const activeScenes = manager.getScenes(true); // Only active
227
const allScenes = manager.getScenes(false); // All scenes
228
229
// Scene queries
230
const isActive = manager.isActive('GameScene');
231
const isVisible = manager.isVisible('UIScene');
232
const isSleeping = manager.isSleeping('BackgroundScene');
233
234
// Scene operations
235
manager.start('GameScene', { level: 2 });
236
manager.pause('GameScene');
237
manager.resume('GameScene');
238
manager.sleep('GameScene');
239
manager.wake('GameScene');
240
manager.stop('GameScene');
241
manager.switch('MenuScene');
242
243
// Batch operations
244
manager.pauseAll();
245
manager.resumeAll();
246
247
// Scene events on manager
248
manager.on('start', (event, scene) => {
249
console.log('Scene started:', scene.sys.settings.key);
250
});
251
252
manager.on('ready', (event, scene) => {
253
console.log('Scene ready:', scene.sys.settings.key);
254
});
255
}
256
}
257
```
258
259
## Multiple Scene Architecture
260
261
### Parallel Scenes
262
Run multiple scenes simultaneously for layered functionality:
263
264
```javascript { .api }
265
// Main game scene
266
class GameScene extends Phaser.Scene {
267
constructor() {
268
super({ key: 'GameScene' });
269
}
270
271
create() {
272
// Set up main game
273
this.add.image(400, 300, 'background');
274
this.player = this.add.sprite(100, 400, 'player');
275
276
// Launch UI overlay
277
this.scene.launch('UIScene');
278
279
// Launch background effects
280
this.scene.launch('ParticleScene');
281
282
// Communication between scenes
283
this.events.on('updateScore', (score) => {
284
this.scene.get('UIScene').events.emit('scoreChanged', score);
285
});
286
}
287
}
288
289
// UI overlay scene
290
class UIScene extends Phaser.Scene {
291
constructor() {
292
super({ key: 'UIScene' });
293
}
294
295
create() {
296
// Create UI elements that stay on top
297
this.scoreText = this.add.text(16, 16, 'Score: 0', {
298
fontSize: '32px',
299
fill: '#000'
300
});
301
302
// Listen for score updates
303
this.events.on('scoreChanged', (score) => {
304
this.scoreText.setText('Score: ' + score);
305
});
306
307
// UI doesn't scroll with camera
308
this.cameras.main.setScroll(0, 0);
309
}
310
}
311
312
// Background effects scene
313
class ParticleScene extends Phaser.Scene {
314
constructor() {
315
super({ key: 'ParticleScene' });
316
}
317
318
create() {
319
// Background particle effects
320
this.particles = this.add.particles(400, 300, 'particle', {
321
speed: 50,
322
lifespan: 2000,
323
quantity: 2,
324
scale: { start: 0.5, end: 0 }
325
});
326
327
// Render behind main scene
328
this.scene.moveDown();
329
}
330
}
331
```
332
333
### Scene Communication
334
Scenes can communicate through events and data:
335
336
```javascript { .api }
337
// Game scene sends events
338
class GameScene extends Phaser.Scene {
339
playerDied() {
340
// Send event to UI
341
this.events.emit('playerDied');
342
343
// Send event to other scenes via scene manager
344
this.scene.manager.scenes.forEach(scene => {
345
if (scene.sys.settings.key !== 'GameScene') {
346
scene.events.emit('gameOver', { score: this.score });
347
}
348
});
349
350
// Switch to game over with data
351
this.scene.start('GameOverScene', {
352
score: this.score,
353
level: this.level,
354
time: this.gameTime
355
});
356
}
357
}
358
359
// UI scene listens for events
360
class UIScene extends Phaser.Scene {
361
create() {
362
// Listen to game scene events
363
const gameScene = this.scene.get('GameScene');
364
gameScene.events.on('playerDied', () => {
365
this.showGameOverUI();
366
});
367
368
// Global scene events
369
this.events.on('gameOver', (data) => {
370
this.displayFinalScore(data.score);
371
});
372
}
373
}
374
```
375
376
## Scene Transitions
377
378
### Transition Effects
379
Create smooth transitions between scenes:
380
381
```javascript { .api }
382
class TransitionScene extends Phaser.Scene {
383
fadeToScene(sceneKey, duration = 1000) {
384
// Fade out current scene
385
this.cameras.main.fadeOut(duration, 0, 0, 0);
386
387
// When fade completes, switch scenes
388
this.cameras.main.once('camerafadeoutcomplete', () => {
389
this.scene.start(sceneKey);
390
});
391
}
392
393
slideToScene(sceneKey, direction = 'left') {
394
const camera = this.cameras.main;
395
const targetX = direction === 'left' ? -800 : 800;
396
397
// Slide camera off screen
398
this.tweens.add({
399
targets: camera,
400
scrollX: targetX,
401
duration: 1000,
402
ease: 'Power2',
403
onComplete: () => {
404
this.scene.start(sceneKey);
405
}
406
});
407
}
408
409
create() {
410
// Transition buttons
411
this.add.text(100, 100, 'Fade Transition', { fontSize: '24px' })
412
.setInteractive()
413
.on('pointerdown', () => {
414
this.fadeToScene('NextScene');
415
});
416
417
this.add.text(100, 150, 'Slide Transition', { fontSize: '24px' })
418
.setInteractive()
419
.on('pointerdown', () => {
420
this.slideToScene('NextScene', 'left');
421
});
422
}
423
}
424
```
425
426
### Loading Scenes
427
Handle asset loading with dedicated loading scenes:
428
429
```javascript { .api }
430
class LoadingScene extends Phaser.Scene {
431
constructor() {
432
super({ key: 'LoadingScene' });
433
}
434
435
preload() {
436
// Create loading UI
437
const progressBar = this.add.graphics();
438
const progressBox = this.add.graphics();
439
progressBox.fillStyle(0x222222);
440
progressBox.fillRect(240, 270, 320, 50);
441
442
const loadingText = this.add.text(400, 240, 'Loading...', {
443
fontSize: '20px',
444
fill: '#ffffff'
445
}).setOrigin(0.5);
446
447
const percentText = this.add.text(400, 300, '0%', {
448
fontSize: '18px',
449
fill: '#ffffff'
450
}).setOrigin(0.5);
451
452
// Update progress
453
this.load.on('progress', (percent) => {
454
progressBar.clear();
455
progressBar.fillStyle(0xffffff);
456
progressBar.fillRect(250, 280, 300 * percent, 30);
457
percentText.setText(Math.round(percent * 100) + '%');
458
});
459
460
this.load.on('complete', () => {
461
loadingText.setText('Complete!');
462
this.time.delayedCall(500, () => {
463
this.scene.start('GameScene');
464
});
465
});
466
467
// Load game assets
468
this.loadGameAssets();
469
}
470
471
loadGameAssets() {
472
this.load.image('background', 'assets/bg.jpg');
473
this.load.spritesheet('player', 'assets/player.png', {
474
frameWidth: 32,
475
frameHeight: 48
476
});
477
this.load.audio('music', 'assets/music.mp3');
478
// ... more assets
479
}
480
}
481
```
482
483
## Scene Data Management
484
485
### Scene Data
486
Each scene has its own data manager for storing scene-specific data:
487
488
```javascript { .api }
489
class DataScene extends Phaser.Scene {
490
create() {
491
// Set scene data
492
this.data.set('score', 100);
493
this.data.set('level', 1);
494
this.data.set('player', {
495
name: 'Hero',
496
health: 100,
497
mana: 50
498
});
499
500
// Get scene data
501
const score = this.data.get('score');
502
const player = this.data.get('player');
503
504
// Increment values
505
this.data.inc('score', 10);
506
this.data.inc('level');
507
508
// Toggle boolean
509
this.data.toggle('paused');
510
511
// Listen for data changes
512
this.data.on('setdata', (parent, key, value) => {
513
console.log('Data set:', key, value);
514
});
515
516
this.data.on('changedata-score', (parent, value, prevValue) => {
517
console.log('Score changed from', prevValue, 'to', value);
518
});
519
520
// Remove data
521
this.data.remove('temporaryValue');
522
523
// Reset all data
524
this.data.reset();
525
}
526
}
527
```
528
529
### Global Registry
530
Access game-wide data through the global registry:
531
532
```javascript { .api }
533
class RegistryScene extends Phaser.Scene {
534
create() {
535
// Global data accessible from any scene
536
this.registry.set('playerName', 'Hero');
537
this.registry.set('highScore', 5000);
538
this.registry.set('unlockedLevels', [1, 2, 3]);
539
540
// Get global data
541
const playerName = this.registry.get('playerName');
542
const highScore = this.registry.get('highScore');
543
544
// Listen for global data changes
545
this.registry.on('setdata', (parent, key, value) => {
546
if (key === 'highScore') {
547
this.updateHighScoreDisplay(value);
548
}
549
});
550
551
// Any scene can update global data
552
this.registry.inc('highScore', 100);
553
}
554
}
555
```
556
557
## Scene Systems
558
559
### Scene Systems Access
560
Each scene has access to various game systems:
561
562
```javascript { .api }
563
class SystemsScene extends Phaser.Scene {
564
create() {
565
// Core systems
566
const game = this.game; // Game instance
567
const scene = this.scene; // Scene plugin
568
const anims = this.anims; // Animation manager
569
const cache = this.cache; // Cache manager
570
const registry = this.registry; // Global registry
571
const sound = this.sound; // Sound manager
572
const textures = this.textures; // Texture manager
573
574
// Scene-specific systems
575
const cameras = this.cameras; // Camera manager
576
const add = this.add; // GameObject factory
577
const make = this.make; // GameObject creator
578
const physics = this.physics; // Physics world
579
const input = this.input; // Input manager
580
const load = this.load; // Loader plugin
581
const time = this.time; // Clock
582
const tweens = this.tweens; // Tween manager
583
const lights = this.lights; // Lights system
584
const data = this.data; // Scene data manager
585
586
// Display management
587
const children = this.children; // Display list
588
const displayList = this.displayList; // Same as children
589
const updateList = this.updateList; // Update list
590
591
// Events
592
const events = this.events; // Scene events
593
}
594
}
595
```
596
597
### Custom Scene Systems
598
Extend scenes with custom functionality:
599
600
```javascript { .api }
601
class CustomScene extends Phaser.Scene {
602
constructor(config) {
603
super(config);
604
605
// Custom properties
606
this.gameState = 'playing';
607
this.enemies = [];
608
this.powerUps = [];
609
}
610
611
init() {
612
// Custom initialization
613
this.setupCustomSystems();
614
}
615
616
setupCustomSystems() {
617
// Custom enemy manager
618
this.enemyManager = {
619
spawn: (x, y, type) => {
620
const enemy = this.add.sprite(x, y, type);
621
this.enemies.push(enemy);
622
return enemy;
623
},
624
625
update: () => {
626
this.enemies.forEach(enemy => {
627
// Update enemy logic
628
});
629
},
630
631
removeAll: () => {
632
this.enemies.forEach(enemy => enemy.destroy());
633
this.enemies = [];
634
}
635
};
636
637
// Custom event system
638
this.gameEvents = new Phaser.Events.EventEmitter();
639
this.gameEvents.on('enemyDefeated', this.onEnemyDefeated, this);
640
this.gameEvents.on('powerUpCollected', this.onPowerUpCollected, this);
641
}
642
643
update(time, delta) {
644
if (this.gameState === 'playing') {
645
this.enemyManager.update();
646
}
647
}
648
649
onEnemyDefeated(enemy) {
650
// Handle enemy defeat
651
this.data.inc('score', 100);
652
this.gameEvents.emit('scoreUpdated', this.data.get('score'));
653
}
654
655
onPowerUpCollected(powerUp) {
656
// Handle power-up collection
657
this.data.set('powerUpActive', true);
658
this.time.delayedCall(5000, () => {
659
this.data.set('powerUpActive', false);
660
});
661
}
662
}
663
```
664
665
This comprehensive scene management system provides the structure and flexibility needed to create complex, multi-state games with smooth transitions and robust data management.