圆线碰撞检测问题


11

我目前正在开发一个突破克隆,我遇到了障碍,无法正确检测球(圆)和砖(凸多边形)之间的碰撞。我正在使用“圆线碰撞检测”测试,其中每条线代表凸多边形砖上的边缘。

在大多数情况下,Circle-Line测试可以正常工作,并且可以正确解决碰撞点。

碰撞检测正常工作。

但是,有时候我的碰撞检测代码由于球实际上与砖相交时的负判别而返回false。

碰撞检测失败。

我知道这种方法的效率低下,并且使用了轴对齐的边界框来减少测试砖的数量。我主要关心的是下面的代码中是否存在任何数学错误。

/* 
 * from and to are points at the start and end of the convex polygons edge.
 * This function is called for every edge in the convex polygon until a
 * collision is detected. 
 */

bool circleLineCollision(Vec2f from, Vec2f to)
{
    Vec2f lFrom, lTo, lLine;
    Vec2f line, normal;
    Vec2f intersectPt1, intersectPt2;
    float a, b, c, disc, sqrt_disc, u, v, nn, vn;
    bool one = false, two = false;

    // set line vectors
    lFrom = from - ball.circle.centre;      // localised
    lTo = to - ball.circle.centre;          // localised
    lLine = lFrom - lTo;                    // localised
    line = from - to;

    // calculate a, b & c values
    a = lLine.dot(lLine);
    b = 2 * (lLine.dot(lFrom));
    c = (lFrom.dot(lFrom)) - (ball.circle.radius * ball.circle.radius);

    // discriminant
    disc = (b * b) - (4 * a * c);

    if (disc < 0.0f)
    {
        // no intersections
        return false;
    }
    else if (disc == 0.0f)
    {
        // one intersection
        u = -b / (2 * a);

        intersectPt1 = from + (lLine.scale(u));
        one = pointOnLine(intersectPt1, from, to);

        if (!one)
            return false;
        return true;
    }
    else
    {
        // two intersections
        sqrt_disc = sqrt(disc);
        u = (-b + sqrt_disc) / (2 * a);
        v = (-b - sqrt_disc) / (2 * a);
        intersectPt1 = from + (lLine.scale(u));
        intersectPt2 = from + (lLine.scale(v));

        one = pointOnLine(intersectPt1, from, to);
        two = pointOnLine(intersectPt2, from, to);

        if (!one && !two)
            return false;
        return true;
    }
}

bool pointOnLine(Vec2f p, Vec2f from, Vec2f to)
{
    if (p.x >= min(from.x, to.x) && p.x <= max(from.x, to.x) && 
        p.y >= min(from.y, to.y) && p.y <= max(from.y, to.y))
        return true;
    return false;
}

我找不到lLine和line之间的任何区别...
FxIII 2011年

在计算实际点之前,可以简化并完成pointOnLine测试。
FxIII 2011年

sqrt_disc如何计算?
FxIII

抱歉,当我对向量进行定位时,我一定有些困惑,我没有意识到当向量彼此相减时它们将等于相同。在发布之前,我已经整理了一下代码,却忘记了sqrt_disc = sqrt(disc);重新输入。非常感谢您在下面的回答,它对我有很大帮助。
jazzdawg

Answers:


20

AB的线段可以计算为

P(t)= A + D ·t其中DB - A并且t从0到1

现在,圆以原点为中心(如果需要,将AB移到原点上),半径为r

如果由于某种T优得到了你有一个交点P具有相同长度[R或等价地,该长度P的平方等于R 2

向量的长度平方是由他自己做向量的点积而获得的(确实如此,如果找到一个合适的点积运算,他就可以定义一个新的一致的长度概念)

P · P =(A + D ·t)·(A + D ·t)=

· + 2 · d T + d · d T 2

我们想找出对哪个t取P · P =r²,所以我们最后问自己什么时候

· + 2 · d T + d · d T 2 = R 2

或何时

d · d T 2 + 2 · d T + · -R 2 -A = 0

这是非常著名的二次方程式

at²+ bt + c = 0

a = D · D ; b = 2 A · D和c = A · A-

我们必须检查行列式b²-4ac是否为正,因此我们找到t的2个值,它们给出了交点P(t)。

t必须在0到1之间,否则我们发现解决方案位于通过AB的直线上,但在A之前或B之后

[编辑]

由于其他问题可能会对此答案有所帮助,因此我决定尝试使用一些图像简化此编辑中的推理。 起始条件 这是开始条件。现在集中于A_B

段从A到B

D是将A移到B的向量,因此,如果t在0和1之间,则D ·t是D的“适当分数”,因此点A + D ·t位于A_B段中:当t是在0和1之间,深绿色是t> 1时。

现在,如果我们将圆心移到原点,就可以简化事情。可以总是这样做,因为只需更改坐标系即可保留几何,角度,相交,量度等。

圆心移动到中心

现在,我们有了一种简单的方法来计算t变化时P的长度,并说出哪个t P越过圆的边界。

例子

如您所见,P'的长度大于r,而P“的长度小于r。由于向量长度和r均为正数,因此大于或小于被保留的阶数的关系是我们计算长度之间的关系平方和半径平方P * 1P * 2是使| P |²等于r²的点

如预编辑部分所述,我们得出了一个二次方程式,其中t是我们的变量。众所周知,t的解值的范围是从t是一对复数的情况开始-这意味着没有交点;当t是两个相等解时-这意味着存在一个交集;有两个不同的解决方案的情况-这意味着有两个交叉点。

判别是用来区分以前的状态和有效性测试在T做,看它是否有效的交集,但我们的部分之外-即解决方案T已经成为真正的0和1之间,以被认为是正确的路口那年秋天在段A_B中


3
这是使用正确的算法。关于它如何工作的非常好的描述可以在Real Time Rendering 第三版,第787至791页中找到。如果可以在库中找到它,那么值得一看。
达西·雷纳

4
在对此答案的第8次投票中,我达到了2k的声誉点。我非常感谢您对我的信任。这既是对我的努力的认可,也是对继续努力以产生最高质量答案的激励。谢谢
FxIII,2011年

等等,这是否正确说明了两个极端情况?例如,一个圆可以越过t0 <= t <= t1之外的直线所定义的平面,但稍后会撞到该线段的端点。您需要检查线终点和圆弧路径之间的最小距离。如果该距离小于圆半径,则说明该线已被击中。
达西·雷纳

@DarcyRayner您的意思是两个点都在圆形区域内时的情况吗?
FxIII
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.