首先,对于轴对齐的矩形,Kevin Reid的答案是最好的,而算法是最快的。
其次,对于简单形状,请使用相对速度(如下所示)和分离轴定理进行碰撞检测。它会告诉您在直线运动(无旋转)的情况下是否发生碰撞。而且,如果有轮换,那么您需要一个很小的时间步来使其精确。现在,回答问题:
一般情况下如何判断两个凸形是否相交?
我将为您提供一种适用于所有凸形而不只是六边形的算法。
假设X和Y是两个凸形。它们相交当且仅当他们有一个共同的点,即有一点X ∈ X和点ÿ∈Ÿ使得X = Y。如果将空间视为向量空间,则相当于说x-y = 0。现在我们开始从事Minkowski的业务:
的Minkowski求和的X和ÿ是一组所有的X + Y为X∈X和ÿ∈ÿ。
X和Y的示例
X,Y及其Minkowski和X + Y
假设(-Y)是y∈Y 的所有-y的集合,则给定上一段,当且仅当X +(-Y)包含0(即原点)时,X和Y相交。
旁注:为什么我写X +(-Y )而不是X-Y?好了,因为在数学中,有一个名为的闵可夫斯基差的操作一个和乙它有时书面说明X - Y却一无所有做的集合中的所有的说明X - Y的X∈X和Y ^∈ÿ(真正的闵可夫斯基区别要复杂一些)。
因此,我们想计算X和-Y的Minkowski和,并确定它是否包含原点。与任何其他点相比,原点都不是特殊的,因此要确定原点是否在某个域内,我们使用一种算法,该算法可以告诉我们任何给定点是否属于该域。
X和Y的Minkowski总和具有很酷的性质,即如果X和Y是凸的,则X + Y也是。与确定一个点是否属于凸集相比,确定该点是否属于凸集要容易得多。
我们不可能计算所有的说明X - Y的X∈X和Y ^∈ÿ因为有这样的点的无限X和Ÿ,所以希望,因为X,ÿ和X + Y凹凸有致,我们可以只使用定义形状X和Y的“最外”点,它们是它们的顶点,我们将获得X + Y的最外点,以及更多一些点。
这些附加点被X + Y的最外面的点“包围”,因此它们对定义新获得的凸形没有帮助。我们说他们没有定义点集的“ 凸包 ”。因此,我们要做的是摆脱它们,为最终的算法做准备,该算法告诉我们原点是否在凸包内。
X + Y的凸包。我们删除了“内部”顶点。
因此,我们得到
第一种天真的算法
boolean intersect(Shape X, Shape Y) {
SetOfVertices minkowski = new SetOfVertices();
for (Vertice x in X) {
for (Vertice y in Y) {
minkowski.addVertice(x-y);
}
}
return contains(convexHull(minkowski), Vector2D(0,0));
}
循环显然具有复杂度O(mn),其中m和n是每种形状的顶点数。该minkoswki
集合最多包含mn个元素。该convexHull
算法的复杂度取决于您使用的算法,您可以针对O(k log(k)),其中k是点集的大小,因此在本例中,我们得到O(mn log(mn) )。该contains
算法的复杂度与凸包的边(在2D中)或面(在3D中)的数量成线性关系,因此它确实取决于您的起始形状,但不会大于O(mn)。
我会让你用谷歌搜索contains
凸形算法,这是一种很常见的算法。如果有时间,我可以把它放在这里。
但这是我们正在进行的碰撞检测,因此我们可以对其进行很多优化
我们最初有两个物体A和B在一个时间步dt内不旋转地移动(根据我的观察照片可以看出)。我们叫v 一个和v 乙各自的速度一和乙,这对我们的持续时间时间步长期间是不变的DT。我们得到以下内容:
而且,正如您在图片中指出的那样,这些物体在移动时确实会扫过区域(或3D体积):
在时间步长之后,它们最终分别为A'和B'。
要在这里应用我们的幼稚算法,我们只需要计算扫掠的体积即可。但是我们没有这样做。
在B的参考系中,B不移动(du!)。和阿具有一定的速度 相对于乙您通过计算得到v 甲 - v 乙(可以做相反的,计算的相对速度乙在参考帧甲)。
从左到右:基本参考系中的速度;相对速度;计算相对速度。
通过将B视为在其自己的参考系中不动的,您只需计算A在dt期间以其相对速度v A -v B移动时扫过的体积。
这减少了Minkowski和计算中使用的顶点数量(有时会大大减少)。
另一个可能的优化是在计算被一个物体扫过的体积时,假设为A。您不必平移组成A的所有顶点。只有那些属于边(在3D中为面)的顶点外部法线“面”的方向扫掠。当然,当您计算正方形的扫掠面积时,您已经注意到了。您可以使用法线的点积与扫掠方向(必须为正)来判断法线是否朝向扫掠方向。
最后的优化与您关于交叉路口的问题无关,在我们的案例中非常有用。它使用了我们提到的那些相对速度和所谓的分离轴方法。您当然已经知道了。
假设你知道半径的一个和乙相对于它们的质心(即,质量中心,并从它的顶点最远的距离),就像这样:
仅当A的边界圆与B的边界圆匹配时,才可能发生碰撞。我们在这里看到它不会,并且告诉计算机的方法是如下图所示计算C B到I的距离,并确保它大于A和B的半径之和。如果更大,则不会发生碰撞。如果较小,则发生碰撞。
这对于较长的形状效果不佳,但是在正方形或其他此类形状的情况下,排除碰撞是一种很好的启发式方法。
应用于B的分离轴定理和被A扫过的体积确实会告诉您是否发生碰撞。关联算法的复杂度与每个凸形的顶点数量的总和成线性关系,但是当实际处理碰撞时,它的魔力就不那么大了。
我们的新算法更好,它使用相交来帮助检测碰撞,但仍不如分离轴定理有效地判断碰撞是否发生
boolean mayCollide(Body A, Body B) {
Vector2D relativeVelocity = A.velocity - B.velocity;
if (radiiHeuristic(A, B, relativeVelocity)) {
return false; // there is a separating axis between them
}
Volume sweptA = sweptVolume(A, relativeVelocity);
return contains(convexHull(minkowskiMinus(sweptA, B)), Vector2D(0,0));
}
boolean radiiHeuristic(A, B, relativeVelocity)) {
// the code here
}
Volume convexHull(SetOfVertices s) {
// the code here
}
boolean contains(Volume v, Vector2D p) {
// the code here
}
SetOfVertices minkowskiMinus(Body X, Body Y) {
SetOfVertices result = new SetOfVertices();
for (Vertice x in X) {
for (Vertice y in Y) {
result.addVertice(x-y);
}
}
return result;
}