Làm cách nào để phát hiện hướng va chạm của đối tượng hình chữ nhật 2D?


11

Sau câu hỏi này , tôi cần thêm sự giúp đỡ.

Làm thế nào tôi có thể tìm ra phía nào của hình chữ nhật một vụ va chạm đến từ và phản ứng tương ứng?

hình chữ nhật bị va chạm từ mọi phía

Mũi tên màu xanh là đường dẫn mà một số vật thể tròn sẽ đi theo nếu trước và sau khi va chạm với hộp.

Làm thế nào tôi có thể tính toán này?

Câu trả lời:


8

Vì câu hỏi này dựa trên câu hỏi khác của bạn, tôi sẽ đưa ra giải pháp khi hình chữ nhật được căn chỉnh theo trục.

Đầu tiên, bạn xây dựng hình chữ nhật của đối tượng hiện tại với các giá trị sau:

int boxLeft = box.X;
int boxRight = boxLeft + box.Width;
int boxTop = box.Y;
int boxBottom = boxTop + box.Height;

Tiếp theo, bạn phải có vị trí của đối tượng cũ (mà bạn có thể lưu trữ trên mỗi đối tượng hoặc đơn giản là chuyển đến một chức năng) để tạo hình chữ nhật của đối tượng cũ (khi nó không va chạm):

int oldBoxLeft = box.OldX;
int oldBoxRight = oldBoxLeft + box.Width;
int oldBoxTop = box.OldY;
int oldBoxBottom = oldBoxTop + box.Height;

Bây giờ, để biết va chạm đến từ đâu, bạn phải tìm phía mà vị trí cũ không nằm trong khu vực va chạm và vị trí mới của nó. Bởi vì, khi bạn nghĩ về nó, đây là điều xảy ra khi bạn va chạm: một bên không va chạm vào một hình chữ nhật khác.

Đây là cách bạn có thể làm như vậy (các hàm này cho rằng có xung đột. Không nên gọi chúng nếu không có va chạm):

 bool collidedFromLeft(Object otherObj)
{
    return oldBoxRight < otherObj.Left && // was not colliding
           boxRight >= otherObj.Left;
}

Rince và lặp lại.

bool collidedFromRight(Object otherObj)
{
    return oldBoxLeft >= otherObj.Right && // was not colliding
           boxLeft < otherObj.Right;
}

bool collidedFromTop(Object otherObj)
{
    return oldBoxBottom < otherObj.Top && // was not colliding
           boxBottom >= otherObj.Top;
}

bool collidedFromBottom(Object otherObj)
{
    return oldBoxTop >= otherObj.Bottom && // was not colliding
           boxTop < otherObj.Bottom;
}

Bây giờ, để sử dụng thực tế với phản ứng va chạm từ câu hỏi khác:

if (collidedFromTop(otherObj) || collidedFromBottom(otherObj))
    obj.Velocity.Y = -obj.Velocity.Y;
if (collidedFromLeft(otherObj) || collidedFromRight(otherObj))
    obj.Velocity.X = -obj.Velocity.X;

Một lần nữa, đây có thể không phải là giải pháp tốt nhất nhưng đó là cách tôi thường đi để phát hiện va chạm.


Một lần nữa, bạn đã đúng! ; D Cảm ơn ... (gửi cho tôi thêm bưu thiếp của hội đồng quản trị của bạn vào lần tới ... ^ ___ ^)
NemoStein

Ahhh buồn thay, tôi không biết những gì tôi có thể đã sử dụng nó cho .. có thể lần sau!
Jesse Emond

7

Vì câu hỏi giống hệt một phần với câu hỏi này , tôi sẽ sử dụng lại một số phần trong câu trả lời của mình để thử trả lời câu hỏi của bạn.


Hãy xác định một bối cảnh và một số biến để giải thích sau đây dễ hiểu hơn. Biểu mẫu đại diện mà chúng tôi sẽ sử dụng ở đây có thể không phù hợp với hình thức dữ liệu của riêng bạn nhưng sẽ đơn giản hơn để hiểu theo cách này (thực sự bạn có thể sử dụng các phương pháp sau bằng cách sử dụng loại biểu diễn khác khi bạn hiểu nguyên tắc)

Vì vậy, chúng tôi sẽ xem xét Hộp giới hạn căn chỉnh trục (hoặc Hộp giới hạn định hướng ) và Thực thể di chuyển .

  • Hộp Bounding bao gồm 4 mặt và chúng tôi sẽ xác định mỗi mặt là:
    Side1 = [x1, y1, x2, y2] (hai điểm [x1, y1] và [x2, y2])

  • Thực thể chuyển động được định nghĩa là vectơ vận tốc (vị trí + tốc độ):
    vị trí [posX, posY] và tốc độ [speedX, speedY] .


Bạn có thể xác định phía nào của AABB / OBB bị vectơ tấn công bằng phương pháp sau:

  • 1 / Tìm các điểm giao nhau giữa các đường vô hạn đi qua bốn phía của AABB và đường vô hạn đi qua vị trí thực thể (va chạm trước) sử dụng vectơ tốc độ thực thể làm dốc. (Bạn có thể tìm thấy điểm va chạm hoặc số không xác định, tương ứng với các điểm tương đương hoặc các đường chồng chéo)

  • 2 / Khi bạn biết các điểm giao nhau (nếu chúng tồn tại), bạn có thể tìm kiếm các điểm nằm trong giới hạn phân khúc.

  • 3 / Cuối cùng, nếu vẫn còn nhiều điểm trong danh sách (vectơ vận tốc có thể đi qua nhiều phía), bạn có thể tìm kiếm điểm gần nhất từ ​​gốc thực thể bằng cách sử dụng cường độ của vectơ từ giao điểm đến gốc tọa độ.

Sau đó, bạn có thể xác định góc va chạm bằng cách sử dụng một sản phẩm chấm đơn giản.

  • 4 / Tìm góc giữa va chạm bằng cách sử dụng một sản phẩm chấm của vectơ thực thể (có thể là một quả bóng?) Với vectơ bên.

----------

Thêm chi tiết:

  • 1 / Tìm nút giao

    • a / Xác định các dòng vô hạn (Ax + Bx = D) bằng các dạng tham số của chúng (P (t) = Po + tD).

      Điểm gốc: Po = [posX, posY]
      Vectơ chỉ hướng: D = [speedX, speedY]

      A = Dy = speedY
      B = -Dx = -speedX
      D = (Po.x * Dy) - (Po.y * Dx) = (posX speedY ) - (posY speedX)

      Ax + By = D <====> (speedY x) + (-speedX y) = (posX speedY ) - (posY speedX)

      Tôi đã sử dụng các giá trị điểm thực thể để minh họa phương thức, nhưng đây chính xác là cùng một phương thức để xác định các dòng vô hạn 4 cạnh của hộp giới hạn (Sử dụng Po = [x1, y1] và D = [x2-x1; y2-y1] thay thế).

    • b / Tiếp theo, để tìm giao điểm của hai đường thẳng vô hạn ta có thể giải hệ sau:

      A1x + B1x = D1 <== Dòng đi qua điểm thực thể với vectơ tốc độ là dốc.
      A2x + B2x = D2 <== Một trong những dòng đi qua các cạnh AABB.

      trong đó mang lại các tọa độ sau cho việc đánh chặn:

      Đánh chặn x = (( B 2 * D 1) - ( B 1 * D 2)) / (( A 1 * B 2) - ( A 2 * B 1))
      Đánh chặn y = (( A 1 * D 2) - ( A 2 * D 1)) / (( A 1 * B 2) - ( A 2 * B 1))

      Nếu mẫu số ((A1 * B2) - (A2 * B1)) bằng 0, thì cả hai dòng này đều tương đương hoặc chồng chéo, nếu không, bạn nên tìm một giao điểm.

  • 2 / Kiểm tra giới hạn phân khúc. Vì điều này là đơn giản để xác minh, không có nhu cầu để biết thêm chi tiết.

  • 3 / Tìm kiếm điểm gần nhất. Nếu vẫn còn nhiều điểm trong danh sách, chúng ta có thể tìm thấy bên nào gần điểm gốc nhất.

    • a / Xác định vectơ đi từ điểm giao nhau đến điểm gốc thực thể

      V = Po - Int = [Po.x - Int.x; Po.y - Int.y]

    • b / Tính độ lớn vectơ

      | | V | | = sqrt (V.x² + V.y²)

    • c / tìm cái nhỏ nhất
  • 4 / Bây giờ bạn đã biết bên nào sẽ bị tấn công, bạn có thể xác định góc bằng cách sử dụng một sản phẩm chấm.

    • a / Đặt S = [x2-x1; y2-y1] là vectơ bên sẽ được nhấn và E = [speedX; speedY] là vectơ vận tốc thực thể.

      Sử dụng quy tắc sản phẩm chấm vector chúng tôi biết rằng

      S · E = Sx Ex + Sy Ey

      S · E = | | S | | | | E | | θ

      Vì vậy, chúng ta có thể xác định bằng cách thao tác một chút phương trình này ...

      cos θ = (S · E) / (| | S | | | | E | |)

      θ = acos ((S · E) / (| | S | | | | E | |))

      với

      S · E = Sx * Ex + Sy * Ey
      | | S | | = sqrt (Sx² + Sy²)
      | | E | | = sqrt (Ex² + Ey²)


Lưu ý: Như tôi đã nói trong chuỗi câu hỏi khác, đây có lẽ không phải là cách hiệu quả nhất cũng như đơn giản nhất để làm điều đó, đây chỉ là những gì xuất hiện trong đầu và một số phần của toán học có thể giúp ích.

Tôi đã không xác minh bằng một ví dụ OBB cụ thể (tôi đã làm với AABB) nhưng nó cũng hoạt động.


6

Một cách đơn giản là giải quyết va chạm, sau đó lần lượt dịch hộp va chạm của đối tượng đang di chuyển theo từng pixel và xem cái nào dẫn đến va chạm.

Nếu bạn muốn thực hiện "đúng cách" và với các hình dạng va chạm xoay hoặc đa giác tùy ý, tôi khuyên bạn nên đọc lên Định lý trục tách. Chẳng hạn, phần mềm Metanet (những người đã tạo ra trò chơi N) có một bài viết tuyệt vời về SAT . Họ cũng thảo luận về vật lý liên quan.


2

Một cách sẽ là xoay thế giới xung quanh hình chữ nhật của bạn. "Thế giới" trong trường hợp này chỉ là các đối tượng bạn quan tâm: hình chữ nhật và quả bóng. Bạn xoay hình chữ nhật xung quanh tâm của nó cho đến khi giới hạn của nó được căn chỉnh với các trục x- / y, sau đó bạn xoay quả bóng theo cùng một lượng.

Điểm quan trọng ở đây là bạn xoay quả bóng xung quanh tâm của hình chữ nhật chứ không phải của chính nó.

Sau đó, bạn có thể dễ dàng kiểm tra va chạm giống như bạn làm với bất kỳ hình chữ nhật không xoay nào khác.


Một tùy chọn khác là coi hình chữ nhật là bốn phân đoạn dòng riêng biệt và kiểm tra sự va chạm với từng phân đoạn riêng biệt. Điều này cho phép bạn kiểm tra va chạm tìm ra bên nào bị va chạm cùng một lúc.


1

Tôi đã sử dụng các góc cố định trong tính toán của mình, nhưng điều này sẽ giúp bạn một số

void Bullet::Ricochet(C_Rect *r)
{
    C_Line Line;
    //the next two lines are because I detected 
    // a collision in my main loop so I need to take a step back.

    x = x + ceil(speed * ((double)fcos(itofix(angle)) / 65536));
    y = y + ceil(speed * ((double)fsin(itofix(angle)) / 65536));
    C_Point Prev(x,y);

    //the following checks our position to all the lines will give us
    // an answer which line we will hit due to no lines
    // with angles > 90 lines of a rect always shield the other lines.

    Line = r->Get_Closest_Line(Prev);    
    int langle = 0;
    if(!Line.Is_Horizontal())   //we need to rotate the line to a horizontal position
    {
        langle = Line.Get_Point1().Find_Fixed_Angle(Line.Get_Point2());
        angle = angle - langle;  //to give us the new angle of approach
    }
    //at this point the line is horizontal and the bullet is ready to be fixed.
    angle = 256 - angle;
    angle += langle;
}
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.