Ball-Triangle Collision:三角形内小球碰撞边判定的代码错误排查求助
问题分析与解决方案
我仔细梳理了你的代码和思路,发现两个核心问题导致碰撞边的判定结果始终错误,下面逐一说明并给出修正方案:
1. 时间计算公式的低级错误
你在计算碰撞时间t1、t2、t3时,分母部分写错了!把速度向量的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




