在Phaser.js的游戏开发场景中,当存在玩家、敌人、子弹、障碍物等多种需要碰撞检测的对象时,逐个配置碰撞规则会让代码变得冗余且难以维护,通过合理的分组管理多组物理碰撞器是解决这类问题的有效方案。

什么是Phaser.js碰撞分组
Phaser.js的物理系统支持为碰撞器设置分组标识,同一类功能的碰撞器可以归为同一个分组,每个分组有唯一的分组ID和可选的掩码配置。分组机制可以让我们批量配置同类型碰撞器的碰撞规则,不需要逐个对象设置碰撞关系。
分组的核心属性
- group ID:分组的唯一标识,由物理系统自动分配或者手动指定
- collideMask:该分组可以和哪些分组发生碰撞的掩码配置
- 成员管理:分组可以统一管理内部的碰撞器对象,支持批量添加、移除成员
创建和配置碰撞分组
以Phaser的Arcade物理系统为例,我们可以通过物理世界的addCollider方法结合分组来实现多组碰撞器管理,首先创建不同类型的游戏对象并划分分组。
基础分组创建示例
以下代码创建了玩家分组、敌人分组、子弹分组、障碍物分组四个基础分组,并为每个分组添加对应的游戏对象:
// 创建游戏场景
class GameScene extends Phaser.Scene {
create() {
// 启用Arcade物理系统
this.physics.world.setBounds(0, 0, 800, 600);
// 创建四个碰撞分组
this.playerGroup = this.physics.add.group();
this.enemyGroup = this.physics.add.group();
this.bulletGroup = this.physics.add.group();
this.obstacleGroup = this.physics.add.group();
// 添加玩家到玩家分组
const player = this.physics.add.sprite(100, 300, 'player');
player.setCollideWorldBounds(true);
this.playerGroup.add(player);
// 添加敌人到敌人分组
for (let i = 0; i < 5; i++) {
const enemy = this.physics.add.sprite(400 + i * 50, 200, 'enemy');
this.enemyGroup.add(enemy);
}
// 添加障碍物到障碍物分组
const obstacle = this.physics.add.sprite(300, 400, 'obstacle');
obstacle.setImmovable(true);
this.obstacleGroup.add(obstacle);
}
}
配置分组间的碰撞规则
分组创建完成后,需要配置不同分组之间的碰撞关系,避免不必要的碰撞检测消耗性能,同时保证需要的碰撞逻辑正常触发。
常见碰撞规则配置
我们可以通过physics.add.collider方法配置分组间的碰撞,同时可以传入回调函数处理碰撞后的逻辑:
create() {
// 前面的分组创建代码省略
// 玩家和障碍物碰撞,玩家不能穿过障碍物
this.physics.add.collider(this.playerGroup, this.obstacleGroup);
// 玩家和敌人碰撞,触发游戏结束逻辑
this.physics.add.collider(this.playerGroup, this.enemyGroup, (player, enemy) => {
// 碰撞后玩家扣血逻辑
console.log('玩家碰到敌人');
enemy.destroy();
});
// 子弹和敌人碰撞,子弹消失敌人扣血
this.physics.add.collider(this.bulletGroup, this.enemyGroup, (bullet, enemy) => {
bullet.destroy();
enemy.health -= 10;
if (enemy.health <= 0) {
enemy.destroy();
}
}, null, this);
// 子弹和障碍物碰撞,子弹消失
this.physics.add.collider(this.bulletGroup, this.obstacleGroup, (bullet) => {
bullet.destroy();
});
// 敌人和障碍物碰撞,敌人不能穿过障碍物
this.physics.add.collider(this.enemyGroup, this.obstacleGroup);
}
碰撞规则说明
| 分组A | 分组B | 碰撞效果 |
|---|---|---|
| 玩家分组 | 障碍物分组 | 玩家无法穿过障碍物 |
| 玩家分组 | 敌人分组 | 触发玩家受伤逻辑 |
| 子弹分组 | 敌人分组 | 敌人受伤,子弹销毁 |
| 子弹分组 | 障碍物分组 | 子弹销毁 |
| 敌人分组 | 障碍物分组 | 敌人无法穿过障碍物 |
动态管理分组内的碰撞器
游戏运行过程中经常需要动态添加或移除分组内的碰撞器,比如玩家发射子弹时把子弹加入子弹分组,敌人被消灭后从敌人分组移除。
动态添加成员示例
以下代码实现了玩家发射子弹时,将新创建的子弹自动加入子弹分组:
// 玩家发射子弹方法
shoot() {
const bullet = this.physics.add.sprite(
this.player.x + 20,
this.player.y,
'bullet'
);
bullet.setVelocityX(300);
// 将子弹加入子弹分组
this.bulletGroup.add(bullet);
// 子弹超出边界后自动销毁并移除出分组
bullet.setCollideWorldBounds(true);
bullet.body.onWorldBounds = true;
bullet.body.world.on('worldbounds', (body) => {
if (body.gameObject === bullet) {
this.bulletGroup.remove(bullet);
bullet.destroy();
}
});
}
批量操作分组成员
如果需要批量处理分组内的所有碰撞器,比如暂停所有敌人的移动,可以直接遍历分组的children属性:
// 暂停所有敌人的移动
pauseAllEnemies() {
this.enemyGroup.children.each((enemy) => {
enemy.setVelocity(0, 0);
});
}
// 销毁所有子弹
destroyAllBullets() {
this.bulletGroup.clear(true, true);
}
碰撞管理的性能优化
当场景内碰撞器数量较多时,不合理的碰撞管理会导致性能下降,需要注意以下几点优化策略:
- 只配置必要的碰撞规则,不需要碰撞的分组之间不要添加碰撞检测
- 对于不需要物理响应的碰撞检测,使用
overlap代替collider,减少物理计算消耗 - 动态移除已经不需要的碰撞器,避免无效的碰撞检测
- 对于大量同类小碰撞器,可以考虑使用空间划分算法减少检测范围,不过Phaser的Arcade物理系统已经内置了基础的空间优化,常规场景下不需要额外处理
常见问题解答
碰撞回调不触发怎么办
首先检查两个分组是否都添加了碰撞规则,其次确认碰撞器的物理体是否正常启用,最后检查碰撞器的边界是否有重叠,没有重叠的话碰撞不会触发。
如何让两个分组完全不发生碰撞
不调用physics.add.collider或者physics.add.overlap配置这两个分组的碰撞关系即可,默认情况下不同分组的碰撞器不会自动触发碰撞。
分组可以嵌套吗
Phaser.js的原生分组不支持嵌套,如果需要层级化的碰撞管理,可以通过自定义分组标识,在碰撞回调中判断分组类型来实现类似的效果。