关于 scenes
面向 Phaser 开发者的场景系统参考,介绍场景生命周期、注入系统以及通过 ScenePlugin 管理多个场景。适用于组织游戏流程、场景切换和并行运行。
主要功能
- 01场景生命周期与状态管理
- 02场景专属及共享系统
- 03启动、暂停、休眠与恢复场景
- 04场景间传递数据和事件
适用场景
- 01组织关卡与菜单场景
- 02并行运行游戏界面场景
- 03在场景切换时传递游戏数据
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ phaserjs/ phaser --skill scenes安装后在 Claude Code 中输入/scenes确认已加载。
附带文件references/REFERENCE.md
详细介绍
场景
场景是 Phaser 游戏的组织骨架。每个场景都有自己的生命周期(init、preload、create、update),自己的一组注入系统(this.add、this.input、this.cameras 等),并且可以启动、停止、暂停、休眠,或与其他场景并行运行。ScenePlugin(
this.scene)控制所有多场景编排。
关键源码路径: src/scene/Scene.js、src/scene/Systems.js、src/scene/SceneManager.js、src/scene/ScenePlugin.js、src/scene/Settings.js、src/scene/const.js、src/scene/events/、src/scene/InjectionMap.js
相关技能: ../game-setup-and-config/SKILL.md、../loading-assets/SKILL.md、../events-system/SKILL.md
快速开始
// Minimal scene with all lifecycle methods
class GameScene extends Phaser.Scene {
constructor() {
super('GameScene');
}
init(data) {
// Called first. Receives data passed from other scenes.
// 'data' is whatever was passed via scene.start('GameScene', { level: 1 })
this.level = data.level || 1;
}
preload() {
// Called after init. Load assets here.
this.load.image('logo', 'assets/logo.png');
}
create(data) {
// Called after preload completes. Set up game objects.
// 'data' is the same object passed to init.
this.add.image(400, 300, 'logo');
}
update(time, delta) {
// Called every frame while scene is RUNNING.
// time: current time (ms), delta: ms since last frame (smoothed)
}
}
const config = {
width: 800,
height: 600,
scene: [GameScene]
};
const game = new Phaser.Game(config);
核心概念
场景生命周期
生命周期由 SceneManager.bootScene() 和 SceneManager.create() 驱动:
- PENDING (0) - 场景已注册但尚未启动。
- INIT (1) - 如果定义了
scene.init(data),则调用它。数据来自settings.data。 - START (2) -
Systems.start()触发。发出start和ready事件。 - LOADING (3) - 如果定义了
scene.preload(),则调用它。加载器运行。完成后继续进入 create。 - CREATING (4) - 如果定义了
scene.create(data),则调用它。data与 init 相同。 - RUNNING (5) - 每帧调用
scene.update(time, delta)。场景进行渲染。 - PAUSED (6) - 不更新,但仍在渲染。
- SLEEPING (7) - 不更新,不渲染。状态保留在内存中。
- SHUTDOWN (8) - 场景关闭,系统发出
shutdown。可以重新启动。 - DESTROYED (9) - 场景完全销毁。无法重新启动。
状态常量位于 Phaser.Scenes:Phaser.Scenes.PENDING、Phaser.Scenes.RUNNING 等。
不存在 preload 时的流程: init() -> create() -> update() 循环(完全跳过 preload)。
存在 preload 时的流程: init() -> preload() -> 加载器运行 -> create() -> update() 循环。
场景注入属性
这些属性通过 InjectionMap(src/scene/InjectionMap.js)注入到每个场景实例中。左侧是 Systems 键,右侧是场景属性名。
全局管理器(在所有场景之间共享)
| 场景属性 | 类型 | 描述 |
|---|---|---|
this.game | Phaser.Game | Game 实例 |
this.renderer | CanvasRenderer | WebGLRenderer | 当前活动的渲染器 |
this.anims | Phaser.Animations.AnimationManager | 全局动画管理器 |
this.cache | Phaser.Cache.CacheManager | 非图像资源的全局缓存 |
this.plugins | Phaser.Plugins.PluginManager | 全局插件管理器 |
this.registry | Phaser.Data.DataManager | 全局数据管理器(在场景之间共享) |
this.scale | Phaser.Scale.ScaleManager | 全局缩放管理器 |
this.sound | NoAudio | HTML5Audio | WebAudioSoundManager | 声音管理器 |
this.textures | Phaser.Textures.TextureManager | 全局纹理管理器 |
场景专属系统(每个场景独有)
| 场景属性 | 类型 | 描述 |
|---|---|---|
this.sys | Phaser.Scenes.Systems | 场景系统(切勿覆盖) |
this.events | Phaser.Events.EventEmitter | 场景专属事件发射器 |
this.cameras | Phaser.Cameras.Scene2D.CameraManager | 场景摄像机管理器 |
this.add | Phaser.GameObjects.GameObjectFactory | 工厂:创建并添加到显示列表 |
this.make | Phaser.GameObjects.GameObjectCreator | 创建器:创建但不会添加到显示列表 |
this.scene | Phaser.Scenes.ScenePlugin | 场景管理器插件(start/stop/launch) |
this.children | Phaser.GameObjects.DisplayList | 场景显示列表 |
this.lights | Phaser.GameObjects.LightsManager | 场景灯光(插件) |
this.data | Phaser.Data.DataManager | 场景专属数据管理器 |
this.input | Phaser.Input.InputPlugin | 场景输入管理器(插件) |
this.load | Phaser.Loader.LoaderPlugin | 场景加载器(插件) |
this.time | Phaser.Time.Clock | 场景时间/时钟(插件) |
this.tweens | Phaser.Tweens.TweenManager | 场景补间管理器(插件) |
this.physics | Phaser.Physics.Arcade.ArcadePhysics | Arcade 物理(若已配置) |
this.matter | Phaser.Physics.Matter.MatterPhysics | Matter 物理(若已配置) |
自定义注入映射
// Rename injected properties via scene config
const config = {
key: 'MyScene',
map: {
add: 'makeStuff', // this.makeStuff instead of this.add
load: 'loader' // this.loader instead of this.load
}
};
场景管理器
SceneManager(src/scene/SceneManager.js)是游戏级系统。不要直接调用其方法——请改用 this.scene(ScenePlugin)。SceneManager:
- 维护一个有序的场景数组(决定渲染/更新顺序)
- 在每个游戏步进开始时处理一个操作队列(start、stop、sleep 等)
- 所有 ScenePlugin 方法都是排队执行,而非立即执行(它们在下一帧执行)
常见模式
在场景之间切换
// start() -- shuts down current scene, starts target scene
// Current scene gets SHUTDOWN event; target gets full lifecycle
this.scene.start('LevelTwo', { score: 100 });
// restart() -- shuts down and restarts the same scene
this.scene.restart({ score: 0 });
// switch() -- sleeps current scene, starts/wakes target scene
// Current scene state is preserved in memory
this.scene.switch('PauseMenu', { fromScene: 'GameScene' });
// transition() -- animated transition with duration
this.scene.transition({
target: 'LevelTwo',
duration: 1000,
moveAbove: true, // render target above this scene
sleep: false, // false = stop this scene (default), true = sleep it
remove: false, // true = remove this scene from manager after transition
allowInput: false, // allow input on this scene during transition
data: { score: 100 },
onUpdate: function (progress) {
// progress: 0 to 1 over duration
}
});
并行运行场景
// launch() -- starts another scene in parallel (does NOT stop current scene)
this.scene.launch('UIScene', { lives: 3 });
// run() -- smart launcher: starts if not running, resumes if paused, wakes if sleeping
this.scene.run('UIScene', { lives: 3 });
// Control render order of parallel scenes
this.scene.bringToTop('UIScene'); // render last (on top)
this.scene.sendToBack('Background'); // render first (behind)
this.scene.moveAbove('GameScene', 'UIScene'); // UIScene renders above GameScene
this.scene.moveBelow('GameScene', 'Background');
this.scene.moveUp('UIScene'); // move one position up
this.scene.moveDown('UIScene'); // move one position down
this.scene.swapPosition('SceneA', 'SceneB');
在场景之间传递数据
// Method 1: Pass data via start/launch/restart/switch/wake/run
this.scene.start('LevelScene', { level: 5, score: 1200 });
// In LevelScene:
// init(data) { data.level === 5 }
// create(data) { data.score === 1200 }
// Method 2: Access data later via sys.getData()
// In receiving scene, at any time:
const data = this.sys.getData(); // returns settings.data
// Method 3: Global registry (shared across ALL scenes)
// In Scene A:
this.registry.set('playerHP', 100);
// In Scene B:
const hp = this.registry.get('playerHP'); // 100
// Method 4: Scene-specific data manager
this.data.set('localValue', 42);
this.data.get('localValue'); // 42
// Method 5: Direct scene reference
const otherScene = this.scene.get('OtherScene');
otherScene.somePublicProperty;
// Method 6: Events on the global registry
// In Scene A:
this.registry.events.on('changedata-playerHP', (parent, value, previousValue) => {
// react to change
});
// In Scene B:
this.registry.set('playerHP', 50); // triggers the event in Scene A
暂停与恢复
// Pause: stops update loop, still renders
this.scene.pause(); // pause this scene
this.scene.pause('OtherScene'); // pause another scene
// Resume: restart update loop
this.scene.resume();
this.scene.resume('OtherScene', { message: 'welcome back' });
// Sleep: no update AND no render, but state preserved
this.scene.sleep();
this.scene.sleep('OtherScene');
// Wake: restore from sleep
this.scene.wake();
this.scene.wake('OtherScene', { data: 'here' });
// Stop: full shutdown, clears display list and timers
this.scene.stop();
this.scene.stop('OtherScene');
// Check state
this.scene.isActive('OtherScene'); // boolean
this.scene.isPaused('OtherScene'); // boolean
this.scene.isSleeping('OtherScene'); // boolean
this.scene.isVisible('OtherScene'); // boolean
// Control visibility/activity independently
this.scene.setActive(false); // pause
this.scene.setActive(true); // resume
this.scene.setVisible(false); // hide but still update
this.scene.setVisible(true); // show
在运行时添加和移除场景
// Add a new scene dynamically
this.scene.add('BonusLevel', BonusLevelScene, false, { someData: true });
// args: key, sceneConfig, autoStart, data
// Remove a scene entirely (destroyed, cannot be restarted)
this.scene.remove('BonusLevel');
// Spawn multiple instances from one class
for (let i = 0; i < 5; i++) {
this.scene.add('Level' + i, new LevelScene('Level' + i), false);
}
跨场景事件通信
一个场景发出自定义事件;另一个场景进行监听。发出事件的场景是解耦的。
// GameScene emits events
class GameScene extends Phaser.Scene {
collectCoin(coin) {
coin.destroy();
this.events.emit('addScore', 10);
}
}
// UIScene listens (launched in parallel with { active: true })
class UIScene extends Phaser.Scene {
constructor() {
super({ key: 'UIScene', active: true });
}
create() {
this.score = 0;
this.scoreText = this.add.text(10, 10, 'Score: 0');
// Listen for events from GameScene
const gameScene = this.scene.get('GameScene');
gameScene.events.on('addScore', (points) => {
this.score += points;
this.scoreText.setText('Score: ' + this.score);
});
}
}
按场景配置插件
禁用所有默认插件(仅保留核心插件):
super({ key: 'MinimalScene', plugins: [] });
// No this.load, this.tweens, this.time, this.input, this.data, this.lights
仅包含特定插件:
super({ key: 'PreloadScene', plugins: ['Loader'] });
// Only this.load is available; this.tweens, this.time, etc. are undefined
构造函数中的场景配置
向 super() 传入配置,以按场景设置物理、加载器或预加载文件:
class Level1 extends Phaser.Scene {
constructor() {
super({
key: 'Level1',
physics: { arcade: { debug: true, gravity: { y: 200 } } },
loader: { path: 'assets/levels/1/' },
// 'pack' loads files before preload() runs -- good for progress bar assets
pack: {
files: [
{ type: 'image', key: 'bar', url: 'loaderBar.png' }
]
}
});
}
}
安全地重启场景
在 init() 中重置状态,而不是在构造函数中。构造函数只运行一次;init() 在每次启动时都会运行。
class GameScene extends Phaser.Scene {
constructor() {
super('GameScene');
// BAD: this.gameOver = false; -- only set once, not on restart
}
init() {
// GOOD: reset state every time the scene starts
this.gameOver = false;
this.score = 0;
}
create() {
// Clean up on shutdown to avoid stale references
this.events.once('shutdown', () => {
// Clear any arrays holding game object references
this.enemies = [];
});
}
}
事件
所有事件都在 this.events(场景专属的 EventEmitter)上发出,且具有字符串值。通过 this.events.on('eventname', callback) 进行监听。
生命周期事件
| 事件字符串 | 常量 | 回调签名 | 触发时机 |
|---|---|---|---|
'boot' | Phaser.Scenes.Events.BOOT | (sys) | 场景首次实例化时触发一次(用于插件) |
'start' | Phaser.Scenes.Events.START | (sys) | 场景系统启动时(用于插件) |
'ready' | Phaser.Scenes.Events.READY | (sys, data) | 启动之后,供用户代码使用 |
'create' | Phaser.Scenes.Events.CREATE | (scene) | create() 方法运行之后,场景此时处于 RUNNING 状态 |
'preupdate' | Phaser.Scenes.Events.PRE_UPDATE | (time, delta) | 每帧更新之前 |
'update' | Phaser.Scenes.Events.UPDATE | (time, delta) | 每帧更新期间 |
'postupdate' | Phaser.Scenes.Events.POST_UPDATE | (time, delta) | 每帧更新之后 |
'prerender' | Phaser.Scenes.Events.PRE_RENDER | (renderer) | 场景渲染之前 |
'render' | Phaser.Scenes.Events.RENDER | (renderer) | 场景渲染之后 |
状态变更事件
| 事件字符串 | 常量 | 回调签名 | 触发时机 |
|---|---|---|---|
'pause' | Phaser.Scenes.Events.PAUSE | (sys, data) | 场景被暂停 |
'resume' | Phaser.Scenes.Events.RESUME | (sys, data) | 场景被恢复 |
'sleep' | Phaser.Scenes.Events.SLEEP | (sys, data) | 场景进入休眠 |
'wake' | Phaser.Scenes.Events.WAKE | (sys, data) | 场景被唤醒 |
'shutdown' | Phaser.Scenes.Events.SHUTDOWN | (sys, data) | 场景正在关闭 |
'destroy' | Phaser.Scenes.Events.DESTROY | (sys) | 场景正在被销毁 |
过渡事件
| 事件字符串 | 常量 | 回调签名 | 触发对象 |
|---|---|---|---|
'transitionout' | TRANSITION_OUT | (targetScene, duration) | 源场景 |
'transitioninit' | TRANSITION_INIT | (fromScene, duration) | 目标场景(初始化期间) |
'transitionstart' | TRANSITION_START | (fromScene, duration) | 目标场景(create 之后) |
'transitionwake' | TRANSITION_WAKE | (fromScene, duration) | 目标场景(若从休眠中被唤醒) |
'transitioncomplete' | TRANSITION_COMPLETE | (scene) | 目标场景(完成时) |
游戏对象事件
| 事件字符串 | 常量 | 回调签名 |
|---|---|---|
'addedtoscene' | ADDED_TO_SCENE | (gameObject, scene) |
'removedfromscene' | REMOVED_FROM_SCENE | (gameObject, scene) |
有关详细的 API 参考表和源文件映射,请参阅参考指南。
易错点与常见错误
-
操作是排队执行的,而非立即执行。 调用
this.scene.start('X')并不会同步启动 X。它会在下一次 SceneManager 更新时发生。不要在同一帧内依赖目标场景的状态。 -
start()会关闭调用它的场景。this.scene.start('X')会停止当前场景并启动 X。如果你希望两者都运行,请使用launch()或run()。 -
switch()使其休眠,start()将其关闭。switch()会将当前场景保留在内存中(休眠),而start()会触发完全关闭。休眠的场景其事件和引用仍然保持活跃。 -
暂停的场景仍会渲染。
pause()只会停止更新循环。场景仍会被绘制。使用sleep()可同时停止更新和渲染。 -
不要覆盖
this.sys。 Scene 类的 JSDoc 明确警告:覆盖this.sys会破坏一切。 -
不带 key 的
this.scene.start()会重新启动当前场景。 这等价于this.scene.restart()。 -
传递给
start()/launch()的数据在init(data)和create(data)中都可用。 它存储在settings.data中,之后可通过this.sys.getData()访问。 -
休眠的场景仍可接收来自其他场景的事件。 如果场景 A 处于休眠状态,但场景 B 在全局 registry 上触发事件,场景 A 的监听器仍会触发。请小心处理休眠场景上的活跃监听器。
-
场景渲染顺序 = 数组顺序。 数组中靠后的场景渲染在上层。使用
bringToTop()、sendToBack()、moveAbove()、moveBelow()来控制层级。 -
shutdown与destroy的区别。 Shutdown 会让场景进入休眠状态(可以重新启动)。Destroy 会永久移除它。监听'shutdown'以释放那些在重启时应重新创建的资源。监听'destroy'进行最终清理。 -
像
this.physics和this.matter这样的插件属性仅在配置了物理系统时才可用。 否则它们将是 undefined。 -
create事件在create()方法返回之后触发,并且在状态变为 RUNNING 之后。如果你需要在创建后进行设置,请监听此事件。 -
在
init()中重置状态,而不是在构造函数中。 构造函数仅在场景首次实例化时运行一次。init()在每次场景启动/重启时都会运行。请在那里进行状态重置。 -
在
shutdown时进行清理,以避免残留引用。 监听this.events.once('shutdown', ...)来清空持有游戏对象的数组、移除外部事件监听器等。对已销毁游戏对象的残留引用会在重启时导致错误。 -
switch()会重新启动已暂停的场景,而绝不会恢复它。 如果你需要恢复行为,请改用run()。 -
场景以逆序更新,以正序渲染。 顶层场景最先更新(获得输入优先级),但最后渲染(显示在最上层)。在游戏配置的场景数组中确定渲染顺序。