Phaser 3:实现Arcade.Group与世界边界的碰撞检测
解决Phaser 3.55.2中Arcade.Group的世界边界碰撞问题
我需要创建一艘由船身和旗帜组成的飞船(作为Arcade.Group),要求阻止它超出世界边界,尝试两种方案后遇到以下问题:
问题1:组内对象单独碰撞导致位置扭曲
尝试给组内每个对象设置setCollideWorldBounds(true),但每个对象单独计算边界碰撞,导致飞船部件位置扭曲。代码如下:
create(){ // 创建飞船组 this.shipGroup = this.physics.add.group() // 添加船身到组 this.shipGroup.create(400, 500, "1021") // 添加旗帜到组 const mainFlag = this.shipGroup.create(400, 500, "FA1") mainFlag.setOrigin(0.5, 0.8) // 给组内每个对象开启世界边界碰撞 this.shipGroup.children.each((item: any) => item.setCollideWorldBounds(true) ) } update(t: number, dt: number){ if (this.cursor.up.isDown) { this.shipGroup.setVelocity(...) // 此处省略具体速度值 } else { this.shipGroup.setVelocity(0, 0) } }
问题2:边界框的checkWorldBounds()返回值异常
为避免部件错位,尝试给组添加一个边界框对象,仅让它检测边界碰撞,但即使边界框触碰到世界边界,checkWorldBounds()仍返回false,导致无法正确控制移动。代码如下:
create(){ // 创建飞船组 this.shipGroup = this.physics.add.group() // 添加组的边界框 this.shipBox = this.shipGroup.create(400, 500, "bbox") this.shipBox.setCollideWorldBounds(true) this.shipBox.body.onWorldBounds = true // 添加船身到组 this.shipGroup.create(400, 500, "1021") // 添加旗帜到组 const mainFlag = this.shipGroup.create(400, 500, "FA1") mainFlag.setOrigin(0.5, 0.8) // 注释掉了组内所有对象的边界碰撞设置 /*this.shipGroup.children.each((item: any) => item.setCollideWorldBounds(true) )*/ } update(t: number, dt: number){ if (this.cursor.up.isDown && !this.shipBox.body.checkWorldBounds()) { this.shipGroup.setVelocity(...) // 此处省略具体速度值 } else { this.shipGroup.setVelocity(0, 0) } }
解决方案
方案1:基于组整体包围盒的手动边界检测(推荐)
关闭组内所有对象的边界碰撞,通过计算整个组的包围盒,判断是否即将超出世界边界,再控制组的移动:
create(){ this.shipGroup = this.physics.add.group() // 添加飞船部件 this.shipGroup.create(400, 500, "1021") const mainFlag = this.shipGroup.create(400, 500, "FA1") mainFlag.setOrigin(0.5, 0.8) // 关闭组内所有对象的边界碰撞 this.shipGroup.children.each((item: any) => { item.setCollideWorldBounds(false); }); } update(t: number, dt: number){ // 获取组的整体包围盒与世界边界 const groupBounds = this.shipGroup.getBounds(); const worldBounds = this.physics.world.bounds; // 定义移动速度 const moveSpeed = 200; let velX = 0; let velY = 0; // 判断按键与边界,设置速度 if (this.cursor.up.isDown && groupBounds.top > worldBounds.top) { velY = -moveSpeed; } if (this.cursor.down.isDown && groupBounds.bottom < worldBounds.bottom) { velY = moveSpeed; } if (this.cursor.left.isDown && groupBounds.left > worldBounds.left) { velX = -moveSpeed; } if (this.cursor.right.isDown && groupBounds.right < worldBounds.right) { velX = moveSpeed; } this.shipGroup.setVelocity(velX, velY); }
方案2:用不可见核心对象作为锚点
创建一个不可见的物理对象作为飞船核心,开启它的边界碰撞,让组内其他部件跟随核心移动,实现统一的边界限制:
create(){ this.shipGroup = this.physics.add.group() // 创建不可见核心(可使用透明纹理或设置visible为false) this.shipCore = this.shipGroup.create(400, 500, "invisible-texture"); this.shipCore.setVisible(false); this.shipCore.setCollideWorldBounds(true); // 添加飞船部件 const hull = this.shipGroup.create(400, 500, "1021"); const mainFlag = this.shipGroup.create(400, 500, "FA1"); mainFlag.setOrigin(0.5, 0.8); // 让部件与核心绑定(通过碰撞或手动同步位置) this.physics.add.collider(this.shipCore, hull); this.physics.add.collider(this.shipCore, mainFlag); } update(t: number, dt: number){ // 仅控制核心速度,部件会被核心带动 if (this.cursor.up.isDown) { this.shipCore.setVelocityY(-200); } else if (this.cursor.down.isDown) { this.shipCore.setVelocityY(200); } else { this.shipCore.setVelocityY(0); } // 左右方向控制逻辑同理 }
问题2的原因说明
checkWorldBounds()判断的是对象是否已经完全超出边界,而setCollideWorldBounds(true)生效时,Phaser会自动将对象拉回边界内,所以此时该方法会返回false。若要检测对象触碰边界,应监听worldbounds事件:
create(){ // ... 其他代码 this.shipBox.body.onWorldBounds = true; this.physics.world.on('worldbounds', (body) => { if (body.gameObject === this.shipBox) { // 边界框触碰边界时的逻辑 console.log('边界框碰到世界边界'); } }); }
内容的提问来源于stack exchange,提问作者BK52
相关产品推荐
相关产品推荐

