Làm thế nào để phát hiện đường 2D trên đường va chạm?


13

Tôi là một nhà phát triển trò chơi flash hành động, người hơi lạc hậu với toán học, mặc dù tôi thấy vật lý vừa thú vị vừa tuyệt vời.

Để tham khảo, đây là một trò chơi tương tự như trò chơi tôi đang làm: Trò chơi flash chưa được chỉnh sửa

Tôi đã làm cho trò chơi gỡ rối này gần như hoàn thành đầy đủ logic. Nhưng, khi hai đường thẳng giao nhau, tôi cần những đường thẳng giao nhau hoặc 'rối' để hiển thị một màu khác nhau; màu đỏ.

Nó sẽ thực sự là loại người của bạn nếu bạn có thể đề xuất một thuật toán để phát hiện va chạm phân khúc dòng . Về cơ bản, tôi là một người thích suy nghĩ 'trực quan' hơn là 'một cách hợp lý' :)

Chỉnh sửa: Tôi muốn thêm một vài sơ đồ để truyền đạt ý tưởng rõ ràng hơn

không có ngã tư không có ngã tư ngã tư không có ngã tư

PS tôi đang cố gắng để thực hiện một chức năng như

private function isIntersecting(A:Point, B:Point, C:Point, D:Point):Boolean

Cảm ơn trước.


6
Đây là một lời giải thích không trực quan đáng thất vọng về vấn đề này, nhưng nó là một thuật toán và thật có ý nghĩa nếu bạn có thể tự mình đọc toán học của họ: local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d Nó có thể nặng nếu toán học vectơ của bạn yếu. Tôi hiểu - tôi cũng thích giải thích trực quan. Tôi sẽ cố gắng tìm thời gian sau đó để vẽ nguệch ngoạc điều này, nhưng nếu ai đó có khuynh hướng nghệ thuật nhìn thấy liên kết này và có thời gian trước khi tôi làm, hãy truy cập nó!
Anko

Câu trả lời:


18

Tôi sử dụng phương pháp sau đây khá giống với việc thực hiện thuật toán này . Nó ở C # nhưng việc dịch nó sang ActionScript sẽ không đáng kể.

bool IsIntersecting(Point a, Point b, Point c, Point d)
{
    float denominator = ((b.X - a.X) * (d.Y - c.Y)) - ((b.Y - a.Y) * (d.X - c.X));
    float numerator1 = ((a.Y - c.Y) * (d.X - c.X)) - ((a.X - c.X) * (d.Y - c.Y));
    float numerator2 = ((a.Y - c.Y) * (b.X - a.X)) - ((a.X - c.X) * (b.Y - a.Y));

    // Detect coincident lines (has a problem, read below)
    if (denominator == 0) return numerator1 == 0 && numerator2 == 0;

    float r = numerator1 / denominator;
    float s = numerator2 / denominator;

    return (r >= 0 && r <= 1) && (s >= 0 && s <= 1);
}

Có một vấn đề tinh tế với thuật toán, đó là trường hợp hai dòng trùng nhau nhưng không trùng nhau. Thuật toán vẫn trả về một interectioin trong trường hợp đó. Nếu bạn quan tâm đến trường hợp đó, tôi tin rằng câu trả lời này trên stackoverflow có phiên bản phức tạp hơn để giải quyết nó.

Biên tập

Tôi không nhận được kết quả từ thuật toán này, xin lỗi!

Điều đó thật lạ, tôi đã thử nó và nó hoạt động với tôi ngoại trừ trường hợp duy nhất mà tôi đã mô tả ở trên. Sử dụng cùng một phiên bản chính xác mà tôi đã đăng ở trên, tôi đã nhận được những kết quả này khi tôi dùng thử để lái thử:

nhập mô tả hình ảnh ở đây


Tôi không nhận được kết quả từ thuật toán này, xin lỗi!
Vishnu

4
@Vish Bạn có vấn đề gì? Tôi đã kiểm tra bản sao chính xác của thuật toán này trước khi đăng và nó hoạt động hoàn hảo ngoại trừ trường hợp duy nhất được mô tả.
David Gouveia

Sau đó, hãy để tôi thử lại, tôi có thể đã trộn lẫn một số toán học trong đó. Tôi sẽ cho bạn biết sớm. Cảm ơn rất nhiều, nyways :)
Vishnu

1
Tôi đã nhận được kết quả mong muốn từ thuật toán của bạn, cảm ơn @DavidGouveia.
Vishnu

1
Vâng, nhưng bây giờ tôi có một vấn đề khác :)! Tôi cần phải làm cho các đường giao nhau với màu đỏ và màu xanh lá cây. Các giao lộ hoạt động tốt. Nhưng như tôi đã hiểu bây giờ, (mặc dù về mặt toán học) rằng một if-if đơn giản khác sẽ không hoạt động như đặt các đường màu đỏ và màu xanh lá cây cho các đường giao nhau và không giao nhau. Nút tôi đang kéo có cả dòng bên trái và bên phải. Vì vậy, một cái gì đó đã sai ở đâu đó trong khi thay đổi màu sắc của các đường không giao nhau trở lại màu xanh lá cây. Tôi đoán tôi cũng cần một điều kiện khác. Hmmm, dù sao cũng cảm ơn rất nhiều, tôi đánh dấu đây là câu trả lời đúng.
Vishnu

4

Không có sự chia rẽ! Vì vậy, không có vấn đề với độ chính xác cũng như bằng cách chia cho số không.

Đoạn 1 là A đến B Đoạn 2 là C đến D

Một dòng là một dòng không bao giờ kết thúc, phân đoạn dòng là một phần được xác định của dòng đó.

Kiểm tra xem hai hộp giới hạn có giao nhau không: nếu không có giao lộ -> Không có Chữ thập! (tính toán xong, trả về false)

Kiểm tra xem dòng seg 1 có phải là dòng seg 2 hay không và nếu dòng seg 2 đứng trên dòng seg 1 (tức là Đoạn 1 nằm ở cả hai phía của Dòng được xác định bởi Đoạn 2).

Điều này có thể được thực hiện bằng cách dịch tất cả các điểm bằng -A (nghĩa là bạn di chuyển 2 dòng sao cho A nằm trong origo (0,0))

Sau đó, bạn kiểm tra xem điểm C và D có nằm trên các cạnh khác nhau của đường thẳng được xác định bởi 0,0 đến B không

//Cross Product (hope I got it right here)
float fC= (B.x*C.y) - (B.y*C.x); //<0 == to the left, >0 == to the right
float fD= (B.x*D.y) - (B.y*D.x);

if( (fc<0) && (fd<0)) //both to the left  -> No Cross!
if( (fc>0) && (fd>0)) //both to the right -> No Cross!

Nếu bạn chưa có "Không chéo" thì hãy tiếp tục sử dụng không phải A, B so với C, D mà C, D so với A, B (cùng một calcs, chỉ cần trao đổi A và C, B và D), nếu không có "Không có chữ thập!" sau đó bạn có một ngã tư!

Tôi đã tìm kiếm các tính toán chính xác cho sản phẩm chéo và tìm thấy bài đăng trên blog này cũng giải thích phương pháp này.


1
Tôi xin lỗi nhưng tôi không rành lắm về toán học vectơ, tôi đã triển khai thuật toán này như vậy, nhưng không có kết quả, xin lỗi!
Vishnu

1
Nó sẽ hoạt động để có thể nếu bạn có thể cho chúng tôi xem mã của bạn, chúng tôi có thể giúp bạn không?
Valmond

Đẹp! tuy nhiên liên kết bị hỏng
clabe45

Có một cái gì đó bạn có thể thêm vào điều này để có được điểm giao nhau?
SeanRamey

1

Tôi chỉ muốn nói rằng, tôi cần nó cho trò chơi Gamemaker Studio của tôi và nó hoạt động tốt:

///scr_line_collision(x1,y1,x2,y2,x3,y3,x4,y4)

var denominator= ((argument2 - argument0) * (argument7 - argument5)) - ((argument3 - argument1) * (argument6 - argument4));
var numerator1 = ((argument1 - argument5) * (argument6 - argument4)) - ((argument0 - argument4) * (argument7 - argument5));
var numerator2 = ((argument1 - argument5) * (argument2 - argument0)) - ((argument0 - argument4) * (argument3 - argument1));

// Detect coincident lines
if (denominator == 0) {return (numerator1 == 0 && numerator2 == 0)}

var r = numerator1 / denominator;
var s = numerator2 / denominator;

return ((r >= 0 && r <= 1) && (s >= 0 && s <= 1));

Tôi nghĩ rằng câu trả lời này thực sự có thể cải thiện nếu bạn giải thích những gì mã làm.
TomTsagk

1

Câu trả lời được chấp nhận đã trả lời sai trong trường hợp này:

x1 = 0;
y1 = 0;
x2 = 10;
y2 = 10;

x3 = 10.1;
y3 = 10.1;
x4 = 15;
y4 = 15;

Những dòng này rõ ràng không giao nhau, nhưng theo chức năng trong "câu trả lời đúng" thì các dòng làm giao nhau.

Đây là những gì tôi sử dụng:

function do_lines_intersect(px1,py1,px2,py2,px3,py3,px4,py4) {
  var ua = 0.0;
  var ub = 0.0;
  var ud = (py4 - py3) * (px2 - px1) - (px4 - px3) * (py2 - py1);


  if (ud != 0) {
    ua = ((px4 - px3) * (py1 - py3) - (py4 - py3) * (px1 - px3)) / ud;
    ub = ((px2 - px1) * (py1 - py3) - (py2 - py1) * (px1 - px3)) / ud;
        if (ua < 0.0 || ua > 1.0 || ub < 0.0 || ub > 1.0) ua = 0.0;
  }

  return ua;
}

trả về 0 = các đường không giao nhau

trả về> 0 = các đường giao nhau


Cập nhật để trả lời câu hỏi:

Tôi đã không tự tạo mã này. Nó đã hơn 5 tuổi và tôi không biết nguồn gốc là gì. Nhưng..

Tôi nghĩ giá trị trả về là vị trí tương đối của dòng đầu tiên nơi chúng giao nhau (để giải thích nó xấu). Để tính điểm giao nhau, có lẽ bạn có thể sử dụng lerp như thế này:

l = do_lines_intersect(...)
if (l > 0) {
    intersect_pos_x = l * (px2-px1);
    intersect_pos_y = l * (py2-py1);
} else {
    // lines do not cross
}

(TÔI KHÔNG KIỂM TRA NÀY)


Có một phiên bản này trả về điểm giao nhau?
SeanRamey
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.