Phaser 3场景切换后角色失控循环运行问题求助
Phaser场景切换失控问题修复方案
问题原因分析
全局变量导致状态残留
你把postava、keyA、keyD等核心变量定义为全局变量,切换场景时这些变量不会被销毁。比如从rozcestnikScena切回uvodniScena时,全局的keyD可能还保留着“按下”状态,导致角色进入新场景后直接冲向边界,触发混乱的循环切换。场景切换未重置角色状态
切换场景前没有重置角色速度,角色带着之前的移动速度进入新场景,直接触发边界切换条件,形成死循环。代码冗余与变量缺失
- 两个场景重复创建相同key的动画,可能导致动画状态异常;
rozcestnikScena未初始化keyA/keyD等按键变量,直接调用全局旧变量,造成按键逻辑混乱。
具体修复步骤
1. 替换全局变量为场景局部变量
将所有核心变量绑定到场景实例(用this.前缀),彻底避免跨场景状态污染:
修改uvodniScena代码
// 删除顶部全局变量定义:let zeme; let postava; ... class JSScenaPlay extends Phaser.Scene { constructor() { super("uvodniScena"); } create() { this.gzDialog.setText("Ah heck, my car has broken down. I need to go to a nearby town and find somebody who can help."); this.background = this.add.image(0, 0, "pozadiObloha"); this.background.setOrigin(0, 0); // 场景局部变量定义 this.zeme = this.physics.add.staticGroup(); this.zeme.create(config.width / 2, 675, "zeme").refreshBody(); this.postava = this.physics.add.sprite(20, 556, "postava"); this.postava.setBounce(0.1); this.postava.setDepth(5); this.auto = this.physics.add.image(config.width / 2 + 325, 562, "auto"); this.auto.setScale(1.5); this.auto.setDepth(1); // 动画建议统一在Boot场景创建,暂时保留 this.anims.create({ key: "right", frames: this.anims.generateFrameNumbers("postava", { start: 5, end: 8 }), frameRate: 10, repeat: -1 }); this.anims.create({ key: "turn", frames: [{ key: "postava", frame: 4 }], frameRate: 20 }); this.anims.create({ key: "left", frames: this.anims.generateFrameNumbers("postava", { start: 0, end: 3 }), frameRate: 10, repeat: -1 }); this.postava.body.setGravityY(50); this.physics.add.collider(this.postava, this.zeme); this.physics.add.collider(this.auto, this.zeme); this.physics.add.collider(this.postava, this.auto, this.diaOkno, null, this); this.auto.setPushable(false); this.klavesnice = this.input.keyboard.createCursorKeys(); this.keyA = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A); this.keyS = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S); this.keyD = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D); this.keyW = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W); } diaOkno(postava, auto) { postava.anims.play("turn"); this.gzDialog.visible = true; this.gzDialog.setText("I need to find some car mechanic to repair the engine."); } update() { // 角色移动逻辑 if (this.keyA.isDown || this.klavesnice.left.isDown) { this.postava.setVelocityX(-100); this.postava.anims.play("left", true); } else if (this.keyD.isDown || this.klavesnice.right.isDown) { this.postava.setVelocityX(100); this.postava.anims.play("right", true); } else { this.postava.setVelocityX(0); this.postava.anims.play("turn"); } // 跳跃逻辑 if ((this.keyW.isDown && this.postava.body.touching.down) || (this.klavesnice.up.isDown && this.postava.body.touching.down)) { this.postava.setVelocityY(-250); } // 场景切换逻辑:切换前重置速度 if (this.postava.x < 0) { this.postava.setVelocity(0); this.scene.switch("rozcestnikScena"); } } }
修改rozcestnikScena代码
class JSScenaPlayRozcestnik extends Phaser.Scene { constructor() { super("rozcestnikScena"); } create() { this.background = this.add.image(0, 0, "pozadiObloha"); this.background.setOrigin(0, 0); this.zeme = this.physics.add.staticGroup(); this.zeme.create(config.width / 2, 675, "zeme").refreshBody(); // 调整初始位置,避免刚进入就触发切换 this.postava = this.physics.add.sprite(780, 556, "postava"); this.postava.setBounce(0.1); this.postava.setDepth(5); this.klavesnice = this.input.keyboard.createCursorKeys(); this.physics.add.collider(this.postava, this.zeme); this.postava.body.setGravityY(50); // 初始化当前场景的按键变量 this.keyA = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A); this.keyD = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D); this.keyW = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W); } update() { // 角色移动逻辑 if (this.keyA.isDown || this.klavesnice.left.isDown) { this.postava.setVelocityX(-100); this.postava.anims.play("left", true); } else if (this.keyD.isDown || this.klavesnice.right.isDown) { this.postava.setVelocityX(100); this.postava.anims.play("right", true); } else { this.postava.setVelocityX(0); this.postava.anims.play("turn"); } // 跳跃逻辑 if ((this.keyW.isDown && this.postava.body.touching.down) || (this.klavesnice.up.isDown && this.postava.body.touching.down)) { this.postava.setVelocityY(-250); } // 对话框关闭逻辑 if (this.gzDialog?.visible) { this.physics.pause(); this.postava.anims.play("right"); if (this.klavesnice.space.isDown) { this.gzDialog.display(false); this.physics.resume(); } } // 场景切换逻辑:切换前重置速度 if (this.postava.x > 800) { this.postava.setVelocity(0); this.scene.switch("uvodniScena"); } } }
2. 统一创建通用动画
建议新增一个Boot场景,统一加载资源并创建通用动画,避免场景重复创建:
class BootScene extends Phaser.Scene { constructor() { super("boot"); } preload() { // 加载所有游戏资源 this.load.spritesheet("postava", "path/to/postava.png", { frameWidth: 32, frameHeight: 48 }); this.load.image("pozadiObloha", "path/to/pozadiObloha.png"); this.load.image("zeme", "path/to/zeme.png"); this.load.image("auto", "path/to/auto.png"); } create() { // 创建全局通用动画 this.anims.create({ key: "right", frames: this.anims.generateFrameNumbers("postava", { start: 5, end: 8 }), frameRate: 10, repeat: -1 }); this.anims.create({ key: "turn", frames: [{ key: "postava", frame: 4 }], frameRate: 20 }); this.anims.create({ key: "left", frames: this.anims.generateFrameNumbers("postava", { start: 0, end: 3 }), frameRate: 10, repeat: -1 }); // 启动初始场景 this.scene.start("uvodniScena"); } }
3. 优化场景切换的角色位置传递
可以通过场景切换参数传递角色位置,确保目标场景角色位置合理:
// 在uvodniScena的切换逻辑中 if (this.postava.x < 0) { this.postava.setVelocity(0); this.scene.switch("rozcestnikScena", { playerX: 780, playerY: 556 }); } // 在rozcestnikScena的create方法中接收参数 create(data) { this.postava = this.physics.add.sprite(data.playerX || 780, data.playerY || 556, "postava"); }
额外注意事项
- 所有场景内部变量必须绑定到
this,避免全局污染; - 场景切换前必须重置角色速度和物理状态;
- 通用资源(动画、音效)统一在启动场景加载创建,减少冗余。
内容的提问来源于stack exchange,提问作者Anyn
相关产品推荐
相关产品推荐

