Phaser P2物理中Tile碰撞回调函数失效问题排查与修复咨询
问题根源&修复方案
嘿,我帮你定位到问题啦!你的核心问题是Phaser的setTileIndexCallback是专门给Arcade物理系统设计的,和你用的P2物理系统完全不兼容。当你调用this.physics.p2.convertTilemap()把Tilemap转换成P2刚体后,原来基于Arcade的Tile回调机制就彻底失效了,所以hitHealth和hitAmmo自然不会触发。另外还有个小问题:你的回调函数定义在类外面,就算兼容也会因为this指向错误调用失败。
具体修复步骤
1. 把回调函数移到类内部
先把hitHealth和hitAmmo放进mainScene类里,确保this指向正确:
class mainScene extends Phaser.State { // ... 其他代码 ... hitHealth() { console.log("health hit"); } hitAmmo() { console.log("ammo hit"); } // ... 其他代码 ... }
2. 用P2的碰撞事件监听替代Arcade回调
P2物理系统是基于刚体的,所以我们要给转换后的Tile刚体添加碰撞事件监听。在create方法里,转换Tilemap之后,遍历所有Tile刚体,根据Tile ID绑定对应的回调:
create() { // ... 之前的启动P2、加载Tilemap等代码 ... map.setCollisionByIndex(10); map.setCollisionByIndex(405); map.setCollisionByIndex(403); // 转换Tilemap为P2刚体,得到所有Tile的刚体数组 const tileBodies = this.physics.p2.convertTilemap(map, layer); // 遍历刚体,给目标Tile绑定回调 tileBodies.forEach((body) => { const tile = body.tile; if (!tile) return; if (tile.index === 405) { body.onBeginContact.add(this.hitHealth, this); } else if (tile.index === 403) { body.onBeginContact.add(this.hitAmmo, this); } }); // ... 玩家创建、键盘控制代码 ... }
3. 可选优化:避免重复触发(比如拾取道具后消失)
如果希望碰撞只触发一次,比如拾取血包后Tile消失,可以在回调里销毁刚体并移除Tile:
hitHealth() { console.log("health hit"); // 找到碰撞的Tile刚体 const tileBody = Array.from(arguments).find(body => body.tile); if (tileBody) { // 销毁刚体 tileBody.destroy(); // 从Tilemap中移除该Tile map.removeTile(tileBody.tile.x, tileBody.tile.y, layer); } }
完整修正后的代码
我还补充了原代码中缺失的colour和playerSpeed变量,避免运行报错:
let myplayer; class mainScene extends Phaser.State { constructor() { super({ key: 'mainScene' }); this.colour = 0xff0000; // 补充颜色变量 this.playerSpeed = 200; // 补充玩家速度变量 } preload() { this.load.tilemap('map', 'client/maps/gameMap.csv'); this.load.image('tileset', 'client/maps/tileMap.png'); } create() { this.physics.startSystem(Phaser.Physics.P2JS); this.physics.p2.setImpactEvents(true); const map = this.add.tilemap('map', 32, 32); map.addTilesetImage('tileset'); const layer = map.createLayer(0); layer.resizeWorld(); map.setCollisionByIndex(10); map.setCollisionByIndex(405); map.setCollisionByIndex(403); // 转换Tilemap并绑定碰撞回调 const tileBodies = this.physics.p2.convertTilemap(map, layer); tileBodies.forEach((body) => { const tile = body.tile; if (!tile) return; if (tile.index === 405) { body.onBeginContact.add(this.hitHealth, this); } else if (tile.index === 403) { body.onBeginContact.add(this.hitAmmo, this); } }); // 键盘控制 this.Wkey = this.input.keyboard.addKey(Phaser.Keyboard.W); this.Akey = this.input.keyboard.addKey(Phaser.Keyboard.A); this.Skey = this.input.keyboard.addKey(Phaser.Keyboard.S); this.Dkey = this.input.keyboard.addKey(Phaser.Keyboard.D); // 创建玩家 myplayer = new Phaser.Graphics(game); myplayer.beginFill(this.colour); myplayer.drawCircle(0, 0, 20); myplayer.endFill(); this.physics.p2.enable(myplayer); myplayer.body.setCircle(10); myplayer.body.collideWorldBounds = true; myplayer.speed = this.playerSpeed; game.world.add(myplayer); } update() { if (this.Wkey.isDown) { myplayer.body.moveUp(myplayer.speed); } if (this.Akey.isDown) { myplayer.body.moveLeft(myplayer.speed); } if (this.Skey.isDown) { myplayer.body.moveDown(myplayer.speed); } if (this.Dkey.isDown) { myplayer.body.moveRight(myplayer.speed); } } hitHealth() { console.log("health hit"); const tileBody = Array.from(arguments).find(body => body.tile); if (tileBody) { tileBody.destroy(); map.removeTile(tileBody.tile.x, tileBody.tile.y, layer); } } hitAmmo() { console.log("ammo hit"); const tileBody = Array.from(arguments).find(body => body.tile); if (tileBody) { tileBody.destroy(); map.removeTile(tileBody.tile.x, tileBody.tile.y, layer); } } } var gameConfig = { type: Phaser.AUTO, width: 600, height: 600, parent: 'gameDiv', backgroundColor: '#f3cca3', }; let game = new Phaser.Game(gameConfig); game.state.add("main", mainScene); game.state.start("main");
内容的提问来源于stack exchange,提问作者Rafi Levy
相关产品推荐
相关产品推荐

