如何在Phaser 3中实现篮球与篮筐的条件性碰撞控制?
解决方案
你的核心问题是选错了碰撞控制时机,collisionactive事件是在碰撞持续阶段触发的,这时碰撞已经发生,设置pair.isActive = false无法撤销已经产生的碰撞反弹效果。改用动态碰撞掩码的方式,直接控制球与篮筐的碰撞权限,就能实现上升不碰撞、下落才碰撞的需求。
具体修改步骤:
完善碰撞分类配置:在
create方法里,给球、篮筐和地面明确设置碰撞分类与初始掩码,确保初始状态下球只和地面碰撞:// 在create方法开头定义碰撞分类 const BALL_CATEGORY = 0x0001; const RIM_CATEGORY = 0x0002; const GROUND_CATEGORY = 0x0004; // 修改左右篮筐碰撞体的配置 this.leftRim = this.matter.add.circle(width / 2 - 80, 485, 1, { isStatic: true, label: 'leftRim', collisionFilter: { category: RIM_CATEGORY } }); this.rightRim = this.matter.add.circle(width / 2 + 80, 485, 1, { isStatic: true, label: 'rightRim', collisionFilter: { category: RIM_CATEGORY } }); // 修改球的碰撞体配置,初始只与地面碰撞 this.ball = this.matter.add.image( width / 2, height - 50, 'global', 'basketball' ); setHeightAndScale(this.ball, 150); this.ball.setCircle(); this.ball.setBounce(0.5); this.ball.setInteractive({ useHandCursor: true }); this.ball.body.collisionFilter = { category: BALL_CATEGORY, mask: GROUND_CATEGORY }; // 修改地面碰撞体的分类配置 this.ground = this.matter.add.rectangle(width / 2, height, width, 10, { isStatic: true, label: 'ground', collisionFilter: { category: GROUND_CATEGORY } });在update中动态切换碰撞掩码:根据球的Y速度判断运动状态,实时更新球的碰撞掩码,允许或禁止与篮筐碰撞:
update() { if (!this.isGameRunning) { return; } const isFalling = this.ball.body.velocity.y > 0; const RIM_CATEGORY = 0x0002; const GROUND_CATEGORY = 0x0004; // 根据下落状态切换碰撞权限 if (isFalling) { // 下落时允许和篮筐、地面碰撞 this.ball.body.collisionFilter.mask = RIM_CATEGORY | GROUND_CATEGORY; } else { // 上升时只允许和地面碰撞 this.ball.body.collisionFilter.mask = GROUND_CATEGORY; } // 保留原有的边界检测逻辑 const { height, width } = this.game.config; if ( this.ball.y > height || this.ball.y < 0 || this.ball.x > width || this.ball.x < 0 ) { this.resetBall(this.ball); } }移除无效的
collisionactive事件监听:删掉之前的collisionactive相关代码,因为现在通过碰撞掩码直接控制碰撞权限,无需再处理该事件。简化
collisionstart逻辑:由于碰撞只会在球下落时发生,无需再判断isBallFalling状态:this.matter.world.on('collisionstart', (event) => { event.pairs.forEach((pair) => { const { bodyA, bodyB } = pair; // 处理篮筐碰撞得分(此时必然是下落状态) if ( (bodyA.label === 'basketball' && (bodyB.label === 'leftRim' || bodyB.label === 'rightRim')) || (bodyB.label === 'basketball' && (bodyA.label === 'leftRim' || bodyA.label === 'rightRim')) ) { this.handleRimCollision(); } // 保留地面碰撞重置逻辑 if ( (bodyA === this.ball.body && bodyB.label === 'ground') || (bodyB === this.ball.body && bodyA.label === 'ground') ) { this.resetBall(this.ball); } }); });
原理说明:
Phaser Matter.js的碰撞掩码通过二进制位控制,mask字段决定当前物体能与哪些分类的物体产生碰撞。当球上升时(Y速度≤0),我们将掩码设为仅与地面碰撞;下落时(Y速度>0),添加篮筐的碰撞分类,从根源上控制碰撞发生的时机,彻底避免上升阶段的碰撞反弹问题。
内容的提问来源于stack exchange,提问作者Jimmy Blundell
相关产品推荐
相关产品推荐

