我想改进一下碰撞系统。
目前,我是通过检测包围矩形是否相交来判断两个不规则物体是否碰撞。
我想得到对于一个矩形而言相应的椭圆,而对于另一个物体则使用圆形。我找到了一种方法来获取椭圆坐标,但是当我尝试检测它是否与圆相交时遇到了问题。
你知道如何测试一个圆是否与一个椭圆相交的算法吗?
我想改进一下碰撞系统。
目前,我是通过检测包围矩形是否相交来判断两个不规则物体是否碰撞。
我想得到对于一个矩形而言相应的椭圆,而对于另一个物体则使用圆形。我找到了一种方法来获取椭圆坐标,但是当我尝试检测它是否与圆相交时遇到了问题。
你知道如何测试一个圆是否与一个椭圆相交的算法吗?
最小化平方距离等价于最小化距离,而且更容易处理,因为它是x、y的二次多项式。为了解决约束最小化问题,我们引入拉格朗日乘子lambda,并求解方程组。
2 * [ (x,y) -c ] + lambda * Jg(x,y) = 0
g(x,y) = 0
这里的Jg是g的梯度。这是一个包含三个(非线性)未知数x,y和lambda的方程系统:我们可以使用牛顿法解决这个系统,得到的(x,y)是离圆心最近的点。
提示:这两种方法都从技术上解决了极小化到圆心距离的点。因此,找到的点可能是距离圆最远的点,而不是最近的点。对于这两种方法,使用良好的初始猜测(圆的中心对于方法2效果很好;对于方法1,您需要自己解决)将减少此风险。
第三种可能的方法?:直接解决代表圆和椭圆的两个二次方程组的根可能是可行的。如果存在实根,则对象相交。再次使用像牛顿法这样的数值算法直接解决这个系统是行不通的,因为缺乏收敛并不一定意味着不存在实根。然而,对于两个二次方程的两个变量,可能存在一种专门的方法,可以保证找到实根(如果存在)。我自己想不出如何做到这一点,但您可能需要自己研究它(或查看stackoverflow上是否有人可以详细说明)。
椭圆被定义为一组点,这些点到点A和点B的距离之和是常数e(A和B被称为椭圆的焦点)。
所有满足AP + BP小于e的点P都在椭圆内部。
圆被定义为到点C距离为r的点的集合。
以下是圆和椭圆相交的简单测试:
找到P作为圆与线段AC的交点
找到Q作为圆与线段BC的交点
如果AP + BP <= e或AQ + BQ <= e,则圆和椭圆相交(或圆完全位于椭圆内部)
编辑:
根据Martin DeMello的评论并相应调整我的答案后,我进一步思考了这个问题,并发现(带有第二个检查)仍然不能检测到所有交点:
如果圆和椭圆只是轻微地相交(仅比切线多一点),则P和Q将不在椭圆内:
因此,上述描述的测试仅在重叠区域“足够大”时检测到碰撞。也许对于您的实际目的来说已经足够好了,尽管从数学上讲它不是完美的。
我知道现在可能已经太晚了,但我希望这能帮到某些人。我的解决方法是将椭圆插值为n维多边形,然后构造每两个点之间的线段,并查找圆是否与任何线段相交。这种方法的性能并不是最好的,但它很方便且易于实现。
要将椭圆插值为n维多边形,您可以使用:
float delta = (2 * PI) / n;
std::vector<Point*> interpolation;
for(float t = 0; t < (2 * PI); t += delta) {
float x = rx * cos(t) + c->get_x();
float y = ry * sin(t) + c->get_y();
interpolation.push_back(new Point(x, y));
}
c: 椭圆的中心点。 rx: 椭圆 x 轴半径。 ry: 椭圆 y 轴半径。
现在我们有了插值点,就可以找到圆与每两个点之间连线的交点。一种查找线圆交点的方式在这里描述,如果任何一条线与圆相交,则会发生交点。
希望这能帮助任何人。
m[x^2 + (n/m)x] + o[y^2 + (p/o)y] = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2
m[x^2 + (n/m)x + (n/2m)^2 - (n/2m)^2] + o[y^2 + (p/o)y + (p/2o)^2 - (p/2o)^2] = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2
m[(x + n/2m)^2 - (n/2m)^2] + o[(y + p/2o)^2 - (p/2o)^2] = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2
m(x + n/2m)^2 - m(n/2m)^2 + o(y + p/2o)^2 - o(p/2o)^2 = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2
m(x + n/2m)^2 + o(y + p/2o)^2 = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2 + m(n/2m)^2 + o(p/2o)^2
这个系统有一个解决方案,当且仅当11 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2 + m(n/2m)^2 + o(p/2o)^2 >= 0
如果我没有犯代数错误,这就是答案。我不知道你能简化多少,所以如果你要检查很多圆/椭圆,这个解决方案可能会非常耗费计算资源。
xy
中有交叉项(例如,如果它不是轴对齐的)?我认为类似的方法可能会起作用。 - user168715将椭圆的长轴和短轴半径分别增加圆的半径。然后通过将到扩大后椭圆的两个焦点的距离相加来测试给定圆的中心是否位于这个新的更大的椭圆内。
这个算法非常高效。如果给定的圆不与外接椭圆相交,您可以提前退出。这比边界框测试要慢,但是找到非轴对齐椭圆的边界框很棘手。
当E2越大,即椭圆越扁平时,需要的迭代次数就越多。例如,如果椭圆几乎是圆形(例如E2<0.1),那么五次迭代就可以使x、y在a*1e-12以内,但如果椭圆非常扁平,例如E2=0.999,则需要约300次迭代才能获得相同的精度!
最后,给定纬度,可以通过计算(x,y)来计算高度: x = nucos(lat),y = nu(1-E2)*sin(lat) 然后h是从x,y到圆心的距离, h = hypot(X-x,Y-y)
这并不难。user168715的答案基本上是正确的,但是做微积分并不是必要的。只需要用三角函数。
找到两个物体中心之间的角度。使用这个角度,你可以使用极坐标形式找到椭圆上距离圆心最近的点:
(摘自维基百科关于椭圆的文章)
现在比较两个物体中心之间的距离,减去椭圆半径和圆半径。
也许我漏了什么;也许ArcTan/Cos/Sin很慢——但我不这么认为,而且如果需要的话应该有快速逼近方法。