mcpField
S
scenes

scenes

github.com/phaserjs开发40.6k973 次安装10 周前更新
代码仓库

关于 scenes

面向 Phaser 开发者的场景系统参考,介绍场景生命周期、注入系统以及通过 ScenePlugin 管理多个场景。适用于组织游戏流程、场景切换和并行运行。

主要功能

  1. 01场景生命周期与状态管理
  2. 02场景专属及共享系统
  3. 03启动、暂停、休眠与恢复场景
  4. 04场景间传递数据和事件

适用场景

  1. 01组织关卡与菜单场景
  2. 02并行运行游戏界面场景
  3. 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() 驱动:

  1. PENDING (0) - 场景已注册但尚未启动。
  2. INIT (1) - 如果定义了 scene.init(data),则调用它。数据来自 settings.data。
  3. START (2) - Systems.start() 触发。发出 start 和 ready 事件。
  4. LOADING (3) - 如果定义了 scene.preload(),则调用它。加载器运行。完成后继续进入 create。
  5. CREATING (4) - 如果定义了 scene.create(data),则调用它。data 与 init 相同。
  6. RUNNING (5) - 每帧调用 scene.update(time, delta)。场景进行渲染。
  7. PAUSED (6) - 不更新,但仍在渲染。
  8. SLEEPING (7) - 不更新,不渲染。状态保留在内存中。
  9. SHUTDOWN (8) - 场景关闭,系统发出 shutdown。可以重新启动。
  10. 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.gamePhaser.GameGame 实例
this.rendererCanvasRenderer | WebGLRenderer当前活动的渲染器
this.animsPhaser.Animations.AnimationManager全局动画管理器
this.cachePhaser.Cache.CacheManager非图像资源的全局缓存
this.pluginsPhaser.Plugins.PluginManager全局插件管理器
this.registryPhaser.Data.DataManager全局数据管理器(在场景之间共享)
this.scalePhaser.Scale.ScaleManager全局缩放管理器
this.soundNoAudio | HTML5Audio | WebAudioSoundManager声音管理器
this.texturesPhaser.Textures.TextureManager全局纹理管理器
场景专属系统(每个场景独有)
场景属性类型描述
this.sysPhaser.Scenes.Systems场景系统(切勿覆盖)
this.eventsPhaser.Events.EventEmitter场景专属事件发射器
this.camerasPhaser.Cameras.Scene2D.CameraManager场景摄像机管理器
this.addPhaser.GameObjects.GameObjectFactory工厂:创建并添加到显示列表
this.makePhaser.GameObjects.GameObjectCreator创建器:创建但不会添加到显示列表
this.scenePhaser.Scenes.ScenePlugin场景管理器插件(start/stop/launch)
this.childrenPhaser.GameObjects.DisplayList场景显示列表
this.lightsPhaser.GameObjects.LightsManager场景灯光(插件)
this.dataPhaser.Data.DataManager场景专属数据管理器
this.inputPhaser.Input.InputPlugin场景输入管理器(插件)
this.loadPhaser.Loader.LoaderPlugin场景加载器(插件)
this.timePhaser.Time.Clock场景时间/时钟(插件)
this.tweensPhaser.Tweens.TweenManager场景补间管理器(插件)
this.physicsPhaser.Physics.Arcade.ArcadePhysicsArcade 物理(若已配置)
this.matterPhaser.Physics.Matter.MatterPhysicsMatter 物理(若已配置)
自定义注入映射
// 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 参考表和源文件映射,请参阅参考指南。

易错点与常见错误

  1. 操作是排队执行的,而非立即执行。 调用 this.scene.start('X') 并不会同步启动 X。它会在下一次 SceneManager 更新时发生。不要在同一帧内依赖目标场景的状态。

  2. start() 会关闭调用它的场景。 this.scene.start('X') 会停止当前场景并启动 X。如果你希望两者都运行,请使用 launch() 或 run()。

  3. switch() 使其休眠,start() 将其关闭。 switch() 会将当前场景保留在内存中(休眠),而 start() 会触发完全关闭。休眠的场景其事件和引用仍然保持活跃。

  4. 暂停的场景仍会渲染。 pause() 只会停止更新循环。场景仍会被绘制。使用 sleep() 可同时停止更新和渲染。

  5. 不要覆盖 this.sys。 Scene 类的 JSDoc 明确警告:覆盖 this.sys 会破坏一切。

  6. 不带 key 的 this.scene.start() 会重新启动当前场景。 这等价于 this.scene.restart()。

  7. 传递给 start()/launch() 的数据在 init(data) 和 create(data) 中都可用。 它存储在 settings.data 中,之后可通过 this.sys.getData() 访问。

  8. 休眠的场景仍可接收来自其他场景的事件。 如果场景 A 处于休眠状态,但场景 B 在全局 registry 上触发事件,场景 A 的监听器仍会触发。请小心处理休眠场景上的活跃监听器。

  9. 场景渲染顺序 = 数组顺序。 数组中靠后的场景渲染在上层。使用 bringToTop()、sendToBack()、moveAbove()、moveBelow() 来控制层级。

  10. shutdown 与 destroy 的区别。 Shutdown 会让场景进入休眠状态(可以重新启动)。Destroy 会永久移除它。监听 'shutdown' 以释放那些在重启时应重新创建的资源。监听 'destroy' 进行最终清理。

  11. 像 this.physics 和 this.matter 这样的插件属性仅在配置了物理系统时才可用。 否则它们将是 undefined。

  12. create 事件在 create() 方法返回之后触发,并且在状态变为 RUNNING 之后。如果你需要在创建后进行设置,请监听此事件。

  13. 在 init() 中重置状态,而不是在构造函数中。 构造函数仅在场景首次实例化时运行一次。init() 在每次场景启动/重启时都会运行。请在那里进行状态重置。

  14. 在 shutdown 时进行清理,以避免残留引用。 监听 this.events.once('shutdown', ...) 来清空持有游戏对象的数组、移除外部事件监听器等。对已销毁游戏对象的残留引用会在重启时导致错误。

  15. switch() 会重新启动已暂停的场景,而绝不会恢复它。 如果你需要恢复行为,请改用 run()。

  16. 场景以逆序更新,以正序渲染。 顶层场景最先更新(获得输入优先级),但最后渲染(显示在最上层)。在游戏配置的场景数组中确定渲染顺序。

评论

支持 Markdown