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

检查两圆距离 实现Canvas中小球碰撞反弹功能

问题定位及修复方案

现有代码的核心问题

  • 碰撞检测公式错误:勾股定理计算两点距离应为Math.sqrt(dx*dx + dy*dy),原代码写的是减号,导致距离计算完全错误,大量不该触发碰撞的场景也被误判。
  • 重复碰撞判定:双层循环每次都遍历所有小球,会对同一对小球做两次碰撞检测(i和j、j和i),会导致速度被连续翻转两次,相当于没生效甚至出现反向错乱。
  • 碰撞后速度逻辑错误:只翻转x轴速度不符合物理规则,斜向碰撞时效果完全不符合预期,应该沿两个小球球心连线的法线方向计算速度分量再处理。
  • 变量未声明:循环变量i/j、dx/dy等未加let声明,全部泄漏为全局变量,容易触发不可预知的逻辑错误。

修复方案

只需修改gameloop中的小球碰撞检测逻辑即可,替换后的代码如下:

function gameloop() {
  //clear
  c.clearRect(0, 0, canvas.width, canvas.height);
  c.strokeStyle = 'white';

  //move
  for (let i = 0; i < ballcount; i++) {
    const b1 = balls[i];
    b1.x += b1.vx;
    b1.y += b1.vy;

    //wall collision
    if (b1.x <= b1.r) {
      b1.x = b1.r;
      b1.vx *= -1;
    }
    if (b1.y <= b1.r) {
      b1.y = b1.r;
      b1.vy *= -1;
    }
    if (b1.x >= canvas.width - b1.r) {
      b1.x = canvas.width - b1.r;
      b1.vx *= -1;
    }
    if (b1.y >= canvas.height - b1.r) {
      b1.y = canvas.height - b1.r;
      b1.vy *= -1;
    }

    //ball collision  j从i+1开始避免重复判定同一对球
    for (let j = i + 1; j < ballcount; j++) {
      const b2 = balls[j];
      const dx = b2.x - b1.x;
      const dy = b2.y - b1.y;
      const distanceSquare = dx * dx + dy * dy;
      const rsSum = b1.r + b2.r;
      // 直接比较平方值,省略开根号提升性能
      if (distanceSquare < rsSum * rsSum) {
        const distance = Math.sqrt(distanceSquare);
        // 计算碰撞法线单位向量
        const nx = dx / distance;
        const ny = dy / distance;
        // 计算相对速度
        const dvx = b2.vx - b1.vx;
        const dvy = b2.vy - b1.vy;
        // 相对速度在法线方向的投影
        const dotProduct = dvx * nx + dvy * ny;
        // 如果两个球正在远离就不处理,防止重叠时反复翻转速度
        if (dotProduct > 0) continue;
        // 质量相等的弹性碰撞,直接交换法线方向速度分量
        b1.vx += dotProduct * nx;
        b1.vy += dotProduct * ny;
        b2.vx -= dotProduct * nx;
        b2.vy -= dotProduct * ny;
        // 处理重叠偏移,避免球卡在一起
        const overlap = (rsSum - distance) / 2;
        b1.x -= overlap * nx;
        b1.y -= overlap * ny;
        b2.x += overlap * nx;
        b2.y += overlap * ny;
      }
    }
  }

  //draw
  c.beginPath();
  for (let i = 0; i < ballcount; i++) {
    c.moveTo(balls[i].x + balls[i].r, balls[i].y);
    c.arc(balls[i].x, balls[i].y, balls[i].r, 0, Math.PI * 2);
  }
  c.stroke();
}

额外优化建议

  • 小球初始化生成时可增加碰撞判定,避免初始位置就重叠,减少启动时的异常弹跳。
  • 如果后续需要调整碰撞弹性,可以给速度更新逻辑增加弹性系数参数控制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 02:45:02