Phaser多场景切换时setCollisionByProperty报n未定义错误解决
Phaser多场景切换调用setCollisionByProperty报错修复
问题背景
我是Phaser初学者,正在开发一款迷你RPG游戏。已完成地图制作并导出为.json格式文件,使用setCollisionByProperty方法实现精灵与瓦片的碰撞检测。
单场景测试时碰撞功能运行正常,但启用多场景模式、切换返回场景时会抛出如下错误:
Uncaught TypeError: n is undefined
经多轮测试排查,问题与setCollisionByProperty方法的调用相关,相关代码如下:
游戏入口配置代码
import GameScene from "./GameScene.js" import maisonScene from "./maisonScene.js" import Joueur from "./joueur.js" /* * Configuration du jeu */ var config = { type: Phaser.AUTO, width: 800, height: 600, physics :{ default : 'arcade', arcade:{ debug : true, //Pour debug les collisions gravity :{ y:0 }, } }, scene: { preload: preload, create: create, update: update }, pixelArt: true, scene : [maisonScene, GameScene], }; var game = new Phaser.Game(config); export let joueur = new Joueur(); function preload(){} function create(){} function update(){}
场景逻辑代码
GameScene与maisonScene逻辑完全一致,代码如下:
let staticLayer =[] //tableau contenant les calques du paysage export default class GameScene extends Phaser.Scene{ constructor(){ super({key : "GameScene"}) } create(){ //chargement de la carte const carteDuNiveau = this.add.tilemap("carte"); //chargement des tuiles const tileset = carteDuNiveau.addTilesetImage("Citique","tuiles"); //chargement du calque sol staticLayer.push(carteDuNiveau.createLayer("sol",tileset)); //chargement du calque chemin staticLayer.push(carteDuNiveau.createLayer("chemin",tileset)); //chargement du calque fleurs staticLayer.push(carteDuNiveau.createLayer("fleurs",tileset)); player = this.physics.add.sprite(300, 150, 'player'); player.body.setSize(40,22,false) //Redimensionner la hitbox du joueur player.body.setOffset(4,42) //Deplacer la hitbox vers le bas du joueur player.setScale(0.6); staticLayer.forEach((element)=>{ element.setScale(2); element.setCollisionByProperty({ estSolide: true }); // ajout d'une collision entre le joueur et les calques this.physics.add.collider(player, element); }) } }
代码中staticLayer数组用于存储所有地图图层。
问题根因
报错核心原因是staticLayer数组定义在Scene类外部,属于模块级全局变量:
- 每次进入场景执行
create方法时,都会往数组里push新创建的图层对象 - 场景切换离开时,Phaser会自动销毁当前场景的所有游戏对象(包括之前创建的图层),但这些已销毁的失效图层引用仍然残留在全局
staticLayer数组中 - 再次进入场景遍历
staticLayer数组时,会拿到已经被销毁的无效对象,调用setCollisionByProperty方法时就会触发n is undefined的类型错误
另外入口配置里重复定义了scene属性,后面的数组配置会覆盖前面写的preload/create/update空函数配置,属于潜在问题。
修复方案
- 把
staticLayer数组改为场景实例的内部属性,不要放在类外部做全局变量,避免不同场景、不同生命周期的图层对象互相污染。maisonScene按照相同逻辑修改即可,两个场景各自持有独立的图层数组,互不干扰。修改后的场景代码结构如下:
export default class GameScene extends Phaser.Scene{ constructor(){ super({key : "GameScene"}) // 把图层数组定义为场景自身的属性,每个场景实例独立持有 this.staticLayer = [] } create(){ const carteDuNiveau = this.add.tilemap("carte"); const tileset = carteDuNiveau.addTilesetImage("Citique","tuiles"); // 所有push操作都指向当前场景自己的图层数组 this.staticLayer.push(carteDuNiveau.createLayer("sol",tileset)); this.staticLayer.push(carteDuNiveau.createLayer("chemin",tileset)); this.staticLayer.push(carteDuNiveau.createLayer("fleurs",tileset)); // 注意player如果是全局复用的,切换场景时要把player从旧场景物理世界移除,加到当前场景 player = this.physics.add.sprite(300, 150, 'player'); player.body.setSize(40,22,false) player.body.setOffset(4,42) player.setScale(0.6); // 遍历当前场景自己的图层数组 this.staticLayer.forEach((element)=>{ element.setScale(2); element.setCollisionByProperty({ estSolide: true }); this.physics.add.collider(player, element); }) } // 场景关闭时清空数组,彻底避免残留引用 shutdown(){ this.staticLayer.length = 0 } }
- 清理入口配置里重复的
scene属性,删掉无效的空preload/create/update配置,直接保留场景数组配置即可:
var config = { type: Phaser.AUTO, width: 800, height: 600, physics :{ default : 'arcade', arcade:{ debug : true, gravity :{ y:0 }, } }, pixelArt: true, scene : [maisonScene, GameScene], };
- 如果你需要跨场景复用player精灵,记得在场景切换时不要重复创建player,而是把已有的player实例通过
this.add.existing(player)、this.physics.add.existing(player)的方式加入当前场景,避免重复创建精灵引发的额外碰撞问题。
内容的提问来源于stack exchange,提问作者Gwyn
相关产品推荐
相关产品推荐

