2D AABB vs AABB Sweep:ヒットノーマルの計算方法は?


8

2D AABBvsAABBスイープキャストをゲームに実装しましたが、スイープキャストのヒットノーマルを計算するのが困難です。

AABB vs AABB Sweep:ヒットノーマルの計算方法?

AとBの両方のAABB位置とxy min-maxのスイープ方向があり、動作する最初と最後のヒット時間はありますが、衝突エッジまたは法線方向はありません。この特定の問題に対する効率的な解決策を概念化することはできません。何か案は?:)

*編集

これは私がこれまで持ってきたものです- ゴメスクリスター・エリクソンの AABBスイープの単なる一般的な実装です。通常のヒットはないので、通常の計算は私には謎ですが、キャラクターコントローラーに対して衝突応答を生成することはできません。

bool SweepVelAABBvsAABB(AABB a, AABB b, Vector2 v, out Vector2 outVel, out Vector2 norm )
    {
        outVel = v; //Initialise out velocity
        norm = Vector2.zero;

        if( AABBvsAABB(a,b) ) return true; //return early if a,b overlap

        v = -v;
        float hitTime = 0.0f;
        float outTime = 1.0f;

        if(v.x < 0.0f) //sweep is going right
        {
            if(b.max.x < a.min.x) return false;
            if(a.max.x < b.min.x) hitTime = Mathf.Max( (a.max.x - b.min.x) / v.x, hitTime );
            if(b.max.x > a.min.x) outTime = Mathf.Min( (a.min.x - b.max.x) / v.x, outTime );
        }
        else if(v.x > 0.0f) //sweep is going left
        {
            if(b.min.x > a.max.x) return false;
            if(b.max.x < a.min.x) hitTime = Mathf.Max( (a.min.x - b.max.x) / v.x, hitTime );
            if(a.max.x > b.min.x) outTime = Mathf.Min( (a.max.x - b.min.x) / v.x, outTime );
        }

        if(hitTime > outTime) return false;

        //=================================

        if(v.y < 0.0f) //sweep is going up
        {
            if(b.max.y < a.min.y) return false;
            if(a.max.y < b.min.y) hitTime = Mathf.Max( (a.max.y - b.min.y) / v.y, hitTime );
            if(b.max.y > a.min.y) outTime = Mathf.Min( (a.min.y - b.max.y) / v.y, outTime );
        }
        else if(v.y > 0.0f) //sweep is going down
        {
            if(b.min.y > a.max.y) return false;
            if(b.max.y < a.min.y) hitTime = Mathf.Max( (a.min.y - b.max.y) / v.y, hitTime );
            if(a.max.y > b.min.y) outTime = Mathf.Min( (a.max.y - b.min.y) / v.y, outTime );
        }

        if(hitTime > outTime) return false;

        outVel = -v * hitTime;

        return true;
    }

1
オブジェクトボックスですか?彼らは回転できるはずですか?
aaaaaaaaaaaa

回答:


6

分離軸を観察することで、シンプルで効率的な解決策を思い付きました。

AABBvsAABBスイープ-ヒット通常の計算

// Sweep a in the direction of v against b, returns true & info if there was a hit
// ===================================================================
bool SweepBoxBox( AABB a, AABB b, Vector2 v, out Vector2 outVel, out Vector2 hitNormal )
{
    //Initialise out info
    outVel = v;
    hitNormal = Vector2.zero;

    // Return early if a & b are already overlapping
    if( AABBvsAABB(a, b) ) return false;

    // Treat b as stationary, so invert v to get relative velocity
    v = -v;

    float hitTime = 0.0f;
    float outTime = 1.0f;
    Vector2 overlapTime = Vector2.zero;

    // X axis overlap
    if( v.x < 0 )
    {
        if( b.max.x < a.min.x ) return false;
        if( b.max.x > a.min.x ) outTime = Mathf.Min( (a.min.x - b.max.x) / v.x, outTime );

        if( a.max.x < b.min.x )
        {
            overlapTime.x = (a.max.x - b.min.x) / v.x;
            hitTime = Mathf.Max(overlapTime.x, hitTime);
        }
    }
    else if( v.x > 0 )
    {
        if( b.min.x > a.max.x ) return false;
        if( a.max.x > b.min.x ) outTime = Mathf.Min( (a.max.x - b.min.x) / v.x, outTime );

        if( b.max.x < a.min.x )
        {
            overlapTime.x = (a.min.x - b.max.x) / v.x;
            hitTime = Mathf.Max(overlapTime.x, hitTime);
        }
    }

    if( hitTime > outTime ) return false;

    //=================================

    // Y axis overlap
    if( v.y < 0 )
    {
        if( b.max.y < a.min.y ) return false;
        if( b.max.y > a.min.y ) outTime = Mathf.Min( (a.min.y - b.max.y) / v.y, outTime );

        if( a.max.y < b.min.y )
        {
            overlapTime.y = (a.max.y - b.min.y) / v.y;
            hitTime = Mathf.Max(overlapTime.y, hitTime);
        }           
    }
    else if( v.y > 0 )
    {
        if( b.min.y > a.max.y ) return false;
        if( a.max.y > b.min.y ) outTime = Mathf.Min( (a.max.y - b.min.y) / v.y, outTime );

        if( b.max.y < a.min.y )
        {
            overlapTime.y = (a.min.y - b.max.y) / v.y;
            hitTime = Mathf.Max(overlapTime.y, hitTime);
        }
    }

    if( hitTime > outTime ) return false;

    // Scale resulting velocity by normalized hit time
    outVel = -v * hitTime;

    // Hit normal is along axis with the highest overlap time
    if( overlapTime.x > overlapTime.y )
    {
        hitNormal = new Vector2(Mathf.Sign(v.x), 0);
    }
    else
    {
        hitNormal = new Vector2(0, Mathf.Sign(v.y));
    }

    return true;
}

2

法線付きの図

Aのヒット法線がであることに注意してくださいsum(Normals of Vectors of Edges on B involved in collision)。言葉で:

  1. 衝突しているオブジェクト関係するエッジを見つけます。
  2. それらのエッジから頂点を取得します。
  3. それらの頂点の法線を合計します。
  4. 結果のベクトルを単位ベクトルに変換します(正規化)。

「エッジ」は実際には1つの頂点にすぎない場合があることに注意してください(別のボックスのコーナーと衝突しています)。

これはBの通常のヒットにも当てはまることにも注意してください。


確かに、これは法線方向を取得するのに適した方法ですが、問題の1つは、法線方向を計算するために衝突するエッジがないことです。
Larolaro、2012年

aabbとaabbのテストを行い、どのエッジが衝突しているかをさらに確認します。
Gustavo Maciel

残念ながら、エッジをチェックする方法はまさに私を逃れるものです、私はそれらを取得する方法がわからない場合、問題のエッジをチェックすることはできません。
Larolaro、2012年

trueの場合はaabbとaabbを確認し、次に頂点とaabbを確認し、交差するすべての頂点を取得し、頂点ごとにエッジを取得し、法線を計算します。
Gustavo Maciel

私はそれを行うことができますが、「A」は実際には「B」と交差していません。たとえそうであったとしても(スイープするオブジェクトの内側にあるとスイープは無効になります)頂点/エッジはいくつでも得られます実際の衝突エッジとは無関係です。あなたの理論を説明する回答を投稿していただけませんか?
Larolaro、2012年

0

私が正しく理解していれば、これまでのところ、アルゴリズムはAの動きに沿って、AとBがちょうど触れている位置を見つけます。

その位置で、3つの軸すべてでAとBの間の1次元交差テストを実行します。これらの軸の1つ(コーナーの場合は複数)はオーバーラップしません。ヒット法線はその軸に平行で、BからAの方向にある必要があります。

複数の軸にオーバーラップがない場合は、エッジまたはコーナーを完全にヒットしています。オプションを任意に選択するか、「丸みのある」コーナーの結果を合計することができます。


「B」およびヒット時間「A」とのオーバーラップをチェックすると、エッジが互いにほぼ等しいため(計算はエッジを無限にフラッシュします)、ヒット時間にイプシロンを追加しても、交差は次のようになります。常に2つの軸で重なります。
Larolaro、2012

はい、2つの軸でオーバーラップする必要があります。法線に平行なのは、重なり合っていない、またはほとんど重なり合っていない軸です。数値エラーが心配な場合は、最も重なっていない(または重なっていない)軸を選択します。
Kevin Reid、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.