You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PixiJS开发JS浏览器游戏碰撞反弹偶发失效如何解决

PixiJS 火箭与流星碰撞反弹偶发失效排查

问题根因

反弹效果偶发生效,核心是三个逻辑漏洞共同导致的:

  • 同帧碰撞重复处理,速度被反复交换抵消:当前碰撞检测逻辑是「遍历每个物体作为current,再遍历所有其他物体做检测」,同一对碰撞体(比如火箭A、流星B)会被检测两次:第一次current=A、o=B时交换一次速度,第二次current=B、o=A时两个物体仍处于重叠状态,会再次触发速度交换,把速度换回初始值。console中看到的触发记录包含了这两次相反的交换操作,最终速度实际没有变化,自然看不到反弹效果。只有极少数帧里,第一次交换后物体刚好在同帧移动离开重叠区,第二次检测没触发,才会出现反弹效果。
  • 无碰撞分离逻辑,重叠状态持续多帧:检测到碰撞后仅修改了速度,没有把两个嵌在一起的物体推开,只要物体相对速度不够大,后续多帧都会持续处于碰撞触发状态,反复来回交换速度。
  • 无碰撞处理去重标记:没有记录当前帧已经处理过的碰撞对,无法规避重复响应。

修复方案

按以下步骤修改即可稳定实现反弹效果:

  1. 给所有游戏对象添加唯一标识id,用于标记已处理的碰撞对
  2. 每帧碰撞检测前初始化一个空Set,存储当前帧已经处理过的碰撞对key,避免重复处理
  3. 检测到碰撞后,先计算两个物体的重叠深度,将两个物体沿碰撞法线方向推开,彻底脱离重叠状态
  4. 再执行速度交换逻辑,处理完后把碰撞对的key存入Set,跳过后续重复检测
  5. 调整执行顺序:整轮碰撞检测全部完成后,再统一执行所有物体的移动逻辑,避免边检测边移动导致的判断误差

修正后的核心代码

首先在火箭、流星的构造函数中添加唯一id:

let objId = 0
// 构造函数内添加
this.id = objId++

修正后的碰撞检测函数:

export const checkCollision = (current, objects, processedCollisions) => {
    objects.forEach((o) => {
        if (current === o) return
        // 生成碰撞对唯一key,跳过已处理的碰撞对
        const key1 = `${current.id}-${o.id}`
        const key2 = `${o.id}-${current.id}`
        if (processedCollisions.has(key1) || processedCollisions.has(key2)) return

        // 原有AABB碰撞检测逻辑
        const dx = current.x < o.x
            ? o.x - o.width / 2 - (current.x + current.width / 2)
            : current.x - current.width / 2 - (o.x + o.width / 2);
        const dy = current.y < o.y
            ? o.y - o.height / 2 - (current.y + current.height / 2)
            : current.y - current.height / 2 - (o.y + o.height / 2);

        if (dx < 0 && dy < 0) {
            // 新增:碰撞分离逻辑
            const overlapX = -dx
            const overlapY = -dy
            // 沿重叠深度更小的轴分离,更符合视觉碰撞逻辑
            if (overlapX < overlapY) {
                const separateX = overlapX / 2
                current.x += current.x < o.x ? -separateX : separateX
                o.x += current.x < o.x ? separateX : -separateX
            } else {
                const separateY = overlapY / 2
                current.y += current.y < o.y ? -separateY : separateY
                o.y += current.y < o.y ? separateY : -separateY
            }

            // 原有速度交换逻辑
            const cSpeedX = current.speed.x;
            const cSpeedY = current.speed.y;
            const oSpeedX = o.speed.x;
            const oSpeedY = o.speed.y;
            current.speed.x = oSpeedX;
            current.speed.y = oSpeedY;
            o.speed.x = cSpeedX;
            o.speed.y = cSpeedY;

            // 标记该碰撞对本帧已处理
            processedCollisions.add(key1)
        }
    });
}

每帧游戏循环的执行逻辑同步调整:

// 每帧执行
const processedCollisions = new Set()
// 先完成所有碰撞检测与响应
allGameObjects.forEach(obj => {
    checkCollision(obj, allGameObjects, processedCollisions)
})
// 碰撞处理完再统一移动
allGameObjects.forEach(obj => {
    obj.move(delta)
})

若需要更真实的物理效果,可以在速度交换环节加入弹性系数、按照物体质量分配速度变化量,上述修复已经可以彻底解决偶发不反弹的问题。

内容的提问来源于stack exchange,提问作者Finn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.22 16:12:25