You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Ball-Triangle Collision:三角形内小球碰撞边判定的代码错误排查求助

问题分析与解决方案

我仔细梳理了你的代码和思路,发现两个核心问题导致碰撞边的判定结果始终错误,下面逐一说明并给出修正方案:

1. 时间计算公式的低级错误

你在计算碰撞时间t1t2t3时,分母部分写错了!把速度向量的y分量V0.y误写成了小球初始位置的y分量P0.y,这直接导致时间计算完全偏离正确值。

错误代码片段:

let t1 = (C1 - A1 * P0.x - B1 * P0.y) / (A1 * V0.x + B1 * P0.y);
let t2 = (C2 - A2 * P0.x - B2 * P0.y) / (A2 * V0.x + B2 * P0.y);
let t3 = (C3 - A3 * P0.x - B3 * P0.y) / (A3 * V0.x + B3 * P0.y);

修正后代码:

let t1 = (C1 - A1 * P0.x - B1 * P0.y) / (A1 * V0.x + B1 * V0.y);
let t2 = (C2 - A2 * P0.x - B2 * P0.y) / (A2 * V0.x + B2 * V0.y);
let t3 = (C3 - A3 * P0.x - B3 * P0.y) / (A3 * V0.x + B3 * V0.y);

2. 缺少碰撞点的线段范围验证

即使时间计算正确,你还需要确认计算出的碰撞点确实在三角形的边线段上,而不是边的无限延长线上。小球在三角形内部时,公式解出的t可能对应边的延长部分,这种情况不能算作有效碰撞。

完整修正后的碰撞判定逻辑

我把时间修正和线段验证整合到一起,替换你原来的时间筛选与判定代码:

// 修正后的时间计算
let t1 = (C1 - A1 * P0.x - B1 * P0.y) / (A1 * V0.x + B1 * V0.y);
let t2 = (C2 - A2 * P0.x - B2 * P0.y) / (A2 * V0.x + B2 * V0.y);
let t3 = (C3 - A3 * P0.x - B3 * P0.y) / (A3 * V0.x + B3 * V0.y);

// 存储有效碰撞的时间与对应边
let validCollisions = [];

// 验证紫色边(P1-P3)的碰撞点是否在线段范围内
if (t1 > 0) {
  let hitX = P0.x + V0.x * t1;
  let hitY = P0.y + V0.y * t1;
  // 加±0.01是为了规避浮点数精度误差
  let inXRange = hitX >= min(P1x, P3x) - 0.01 && hitX <= max(P1x, P3x) + 0.01;
  let inYRange = hitY >= min(P1y, P3y) - 0.01 && hitY <= max(P1y, P3y) + 0.01;
  if (inXRange && inYRange) {
    validCollisions.push({time: t1, side: "Purple"});
  }
}

// 验证蓝色边(P2-P3)的碰撞点是否在线段范围内
if (t2 > 0) {
  let hitX = P0.x + V0.x * t2;
  let hitY = P0.y + V0.y * t2;
  let inXRange = hitX >= min(P2x, P3x) - 0.01 && hitX <= max(P2x, P3x) + 0.01;
  let inYRange = hitY >= min(P2y, P3y) - 0.01 && hitY <= max(P2y, P3y) + 0.01;
  if (inXRange && inYRange) {
    validCollisions.push({time: t2, side: "Blue"});
  }
}

// 验证绿色边(P1-P2)的碰撞点是否在线段范围内
if (t3 > 0) {
  let hitX = P0.x + V0.x * t3;
  let hitY = P0.y + V0.y * t3;
  let inXRange = hitX >= min(P1x, P2x) - 0.01 && hitX <= max(P1x, P2x) + 0.01;
  let inYRange = hitY >= min(P1y, P2y) - 0.01 && hitY <= max(P1y, P2y) + 0.01;
  if (inXRange && inYRange) {
    validCollisions.push({time: t3, side: "Green"});
  }
}

// 筛选出最早的有效碰撞
if (validCollisions.length > 0) {
  validCollisions.sort((a, b) => a.time - b.time);
  let earliestHit = validCollisions[0];
  fill(earliestHit.side);
  text(`Hit ${earliestHit.side}`, vertices[1].a.x + 50, max(50, vertices[1].a.y - 50));
} else {
  fill("Black");
  text("No valid collision", vertices[1].a.x + 50, max(50, vertices[1].a.y - 50));
}

额外小提示

  • 原来用round(times[i],2)比较时间的方式容易因精度丢失误判,改用原始时间值排序更可靠。
  • 加入的±0.01误差范围是为了处理浮点数计算时的精度问题,避免因微小偏差导致有效碰撞被误判为无效。

把这些修正应用到你的代码中,就能正确判定小球最先碰撞的三角形边了。

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

火山引擎 最新活动