2D AABB vs AABB Sweep: Cách tính hit bình thường?


8

Tôi đã triển khai chương trình quét AABBvsAABB 2D vào trò chơi của mình, tuy nhiên, tôi gặp khó khăn khi tính toán mức độ bình thường của lượt quét.

AABB vs AABB Sweep: Cách tính hit bình thường?

Tôi có hướng quét, cả vị trí AABB a và b và xy min-max, lần đánh đầu tiên và lần cuối để làm việc, nhưng không phải là (các) cạnh va chạm hoặc hướng thông thường. Tôi chỉ không thể khái niệm một giải pháp hiệu quả cho vấn đề cụ thể này. Bất kỳ ý tưởng? :)

*biên tập

Đây là những gì tôi có cho đến nay - chỉ là một triển khai chung của quét AABB của GomezChrister Ericson . Không có cú đánh bình thường, vì vậy trong khi tính toán bình thường là một bí ẩn đối với tôi, tôi không thể tạo ra bất kỳ phản ứng va chạm nào cho bộ điều khiển nhân vật của mình.

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
Là đối tượng của bạn hộp? Họ có nghĩa là có thể xoay?
aaaaaaaaaaaa

Câu trả lời:


6

Tôi đã xoay sở để đưa ra một giải pháp đơn giản và hiệu quả bằng cách quan sát các trục tách biệt.

AABBvsAABB Sweep - Lượt tính toán bình thường

// 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

Sơ đồ với quy tắc

Chú ý cách đánh của A là bình thường sum(Normals of Vectors of Edges on B involved in collision). Theo thứ tự từ:

  1. Tìm các cạnh liên quan đến đối tượng chúng ta đang va chạm .
  2. Từ có được các đỉnh từ các cạnh.
  3. Tính tổng các giá trị của các đỉnh đó.
  4. Biến vectơ kết quả thành một vectơ đơn vị (chuẩn hóa).

Hãy nhớ rằng một 'cạnh' thực sự có thể chỉ là một đỉnh ( chúng ta đang va chạm với góc trên một hộp khác).

Bạn cũng sẽ lưu ý rằng điều này áp dụng cho hit của B bình thường.


À tôi hiểu rồi, đó sẽ là cách tốt để có được hướng bình thường, tuy nhiên, một vấn đề với điều đó là tôi không có cạnh đang va chạm để tính hướng bình thường.
Larolaro

làm bài kiểm tra aabb vs aabb, sau đó kiểm tra thêm các cạnh nào đang va chạm.
Gustavo Maciel

Thật không may, làm thế nào để kiểm tra các cạnh là điều khiến tôi lảng tránh, tôi không thể kiểm tra các cạnh trong câu hỏi nếu tôi không biết cách lấy chúng.
Larolaro

kiểm tra aabb vs aabb nếu đúng, sau đó kiểm tra đỉnh vs aabb, lấy tất cả các đỉnh giao nhau, lấy các cạnh theo các đỉnh, tính toán bình thường.
Gustavo Maciel

Tôi có thể làm điều đó nhưng không có gì là "A" thực sự giao nhau với "B", ngay cả khi nó là (việc quét sẽ trở thành không hợp lệ bên trong đối tượng bạn đang quét) Tôi sẽ nhận được bất kỳ số đỉnh / cạnh nào không liên quan đến các cạnh va chạm thực tế. Bạn có phiền đăng một câu trả lời để giải thích lý thuyết của bạn?
Larolaro

0

Nếu tôi hiểu chính xác, thuật toán của bạn cho đến nay tìm thấy vị trí dọc theo chuyển động của A mà A và B chỉ chạm vào nhau.

Với vị trí đó, thực hiện kiểm tra giao cắt một chiều giữa A và B trên cả ba trục. Một (hoặc nhiều hơn trong các trường hợp góc) của các trục này sẽ không có sự chồng chéo; cú đánh bình thường phải song song với trục đó và theo hướng từ B đến A.

Nếu nhiều hơn một trục không có sự chồng chéo, thì bạn đã đạt được một cạnh hoặc góc hoàn hảo; bạn có thể tùy ý chọn một tùy chọn hoặc tổng hợp kết quả cho một góc "làm tròn".


Kiểm tra trùng lặp với "B" và thời gian nhấn "A" sẽ cho kết quả không nhất quán vì các cạnh khá giống nhau (phép tính trượt các cạnh vô cùng phẳng) ngay cả với một epsilon bổ sung vào thời gian nhấn, giao điểm sẽ là luôn chồng chéo lên nhau qua hai trục.
Larolaro

Vâng, nó nên được chồng lên nhau trên hai trục. Đó là trục không chồng chéo, hoặc hầu như không chồng chéo, song song với bình thường. Nếu bạn lo ngại về lỗi số, thì hãy chọn trục ít trùng lặp nhất (hoặc không chồng lấp).
Kevin Reid
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.