我一直在尝试基于Randy Gaul 的 C++ Impulse Engine实现圆和多边形之间的碰撞检测,非常接近代码,但算法永远不会返回 true。
这是JSFiddle。(为方便起见,正文使用 HTML5 Canvas API 呈现)
代码片段(只是碰撞检测):
const circPoly = (a, b) => {
let data = {},
center = a.pos;
data.contacts = [];
center = b.mat.clone().trans().mult(center.clone().sub(b.pos));
let sep = -Number.MAX_VALUE,
faceNorm = 0;
for (let i = 0; i < b.verts2.length; ++i) {
let sep2 = b.norms[i].dot(center.clone().sub(b.verts2[i]));
if (sep2 > a.radius) return data;
if (sep2 > sep) { sep = sep2; faceNorm = i; }
}
let v1 = b.verts2[faceNorm],
v2 = b.verts2[faceNorm + 1 < b.verts2.length ? faceNorm + 1 : 0];
if (sep < 0.0001) {
data.depth = a.radius;
data.norm = b.mat.clone().mult(b.norms[faceNorm]).neg();
data.contacts[0] = data.norm.clone().vmult(a.pos.clone().sadd(a.radius));
return data;
}
let dot1 = center.clone().sub(v1).dot(v2.clone().sub(v1)),
dot2 = center.clone().sub(v2).dot(v1.clone().sub(v2));
data.depth = a.radius - sep;
if (dot1 <= 0) {
if (center.dist2(v1) > a.radius * a.radius) return data;
let norm = v1.clone().sub(center);
norm = b.mat.clone().mult(norm);
norm.norm();
data.norm = norm;
v1 = b.mat.clone().mult(v1.clone().add(b.pos));
data.contacts[0] = v1;
} else if (dot2 <= 0) {
if (center.dist2(v2) > a.radius * a.radius) return data;
let norm = v2.clone().sub(center);
norm = b.mat.clone().mult(norm);
norm.norm();
data.norm = norm;
v2 = b.mat.clone().mult(v2.clone().add(b.pos));
data.contacts[0] = v2;
} else {
let norm = b.norms[faceNorm];
if (center.clone().sub(v1).dot(norm) > a.radius) return data;
norm = b.mat.clone().mult(norm);
data.norm = norm.clone().neg();
data.contacts[0] = data.norm.clone().vmult(a.pos.clone().sadd(a.radius));
}
return data;
};
请注意,b.verts2
指的是多边形在现实世界坐标中的顶点。
我知道 Vector 类没有问题,但由于我对转换矩阵没有太多经验,因此该类可能是这些错误的根源,尽管它的代码几乎完全来自脉冲引擎也是如此,所以它应该可以工作。如前所述,该算法总是返回 false,即使确实发生了冲突。我在这里做错了什么?我尝试取出早期返回,但这只会返回奇怪的结果,例如带有负坐标的接触点,这显然不太正确。
编辑:修改了我的矢量类的垂直函数,使其与脉冲引擎的工作方式相同(两种方式都是正确的,但我认为一种是顺时针,另一种是逆时针——我还修改了我的顶点以反映逆时针)。不幸的是,它仍然没有通过测试。