サークルライン衝突検出問題


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

実際のポイントを計算する前に、pointOnLineテストを簡略化して実行できます。
FxIII

sqrt_discの計算方法は?
FxIII

FxIII申し訳ありませんが、ベクトルをローカライズするときに少し混乱したに違いありません。ベクトルが互いに減算されたときに同じになることを理解していませんでした。投稿する前にコードを少し整理していましたが、sqrt_disc = sqrt(disc);元に戻すのを忘れていました。以下の回答に感謝します。
jazzdawg

回答:


20

AからBまでのセグメントは、次のように計算できます。

P(t)= A + D・tここで、DB - Aであり、tは0から1までです。

これで、円は原点を中心とし(中心を原点に配置するために必要に応じてAおよびBを移動)、半径rになります。

Pの長さがrと同じである、または同等に、Pの長さの2乗がと等しいことがわかる場合、交差があります。

ベクトルの長さの二乗は、ベクトルの内積を自分で行うことで得られます(これは非常に真実であるため、内積に適した操作が見つかれば、長さの新しい一貫した概念を定義できます)

PP =(A + D・t)・(A + D・t)=

A + 2 AD T + DD

PP =r² となるtを見つけたいので、いつ私たちは自分自身に問いかけることになります

A + 2 AD T + DD t²= R2

またはいつ

DD t²+ 2 AD T + AA -r²= 0

これは非常に有名な二次方程式です

at²+ bt + c = 0

a = DD ; b = 2 ADおよびc = AA -r²

行列式b²-4ac正かどうかを確認する必要があるため、交点P(t)を与えるtの2つの値を見つけます。

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が変数である2次方程式を取得します。既知のtの解の値は、tが2つの複素数の場合の範囲です。つまり、交差がないことを意味します。tが2つの等しい解である場合-交差が1つあることを意味します。2つの異なるソリューションがある場合、つまり2つの交差がある場合。

判別は、以前の状態を区別するために使用され、妥当性検査は、それならば、有効な交差点が、私たちのセグメントの外を見るためにトンで行われる-すなわち解tは実数である必要があり、0と1の間の適切な交差点の秋その検討しますセグメントA_B内


3
これは正しいアルゴリズムです。それがどのように機能するかについての非常に良い説明は、リアルタイムレンダリング第3版、787〜791ページにあります。ライブラリで見つけることができれば、一見の価値があります。
Darcy Rayner、

4
この回答に対する8番目の賛成投票で、私は2kの評判ポイントに達しました。あなたが私に与えてくれた信頼に大いに感謝します。これは私の努力に対する認識であり、最高品質の答えを生み出すために最善を尽くし続ける刺激でもあります。ありがとう
FxIII、2011年

ちょっと待ってください、これは2つのコーナーケースを正しく説明していますか?たとえば、円はt0 <= t <= t1の外側の線によって定義される平面と交差する場合がありますが、少し後で線セグメントの端点にヒットします。線の端点と円のパスの間の最小距離を確認する必要があります。その距離が円の半径よりも小さい場合、その線はヒットしています。
Darcy Rayner、2011年

@DarcyRaynerは、両方の点が円領域の内側にある場合を意味しますか?
FxIII 2011年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.