Phát hiện va chạm Quad dựa trên cây và lưới


27

Tôi đang tạo một trò chơi loại co-op 4 người chơi và tôi sắp triển khai mã phát hiện va chạm. Tôi đã đọc rất nhiều bài báo và nội dung về cách xử lý phát hiện va chạm, nhưng tôi gặp khó khăn trong việc tìm ra những gì cần phải làm với. Có vẻ như cây tứ giác là cách phổ biến nhất để đi, nhưng trong một số tài nguyên họ đề cập đến giải pháp dựa trên lưới. Vì đã sử dụng lưới để phát hiện trong một trò chơi trước đó, tôi cảm thấy thoải mái với điều đó, nhưng nó có thực sự tốt hơn cây tứ giác không? Tôi không chắc chắn cung cấp hiệu suất tốt nhất và tôi cũng đã chạy một chút điểm chuẩn, không có nhiều khác biệt giữa cả hai giải pháp.

Cái này tốt hơn những cái khác phải không ? hay thanh lịch hơn? Tôi thực sự không chắc chắn nên sử dụng cái nào.

Mọi lời khuyên đều được chào đón. Cảm ơn.

Câu trả lời:


31

Câu trả lời đúng phụ thuộc một chút vào trò chơi thực tế mà bạn thiết kế và việc chọn một trò chơi khác thực sự sẽ đòi hỏi phải thực hiện cả hai và thực hiện hồ sơ để tìm ra trò chơi nào hiệu quả hơn về không gian hoặc thời gian trên trò chơi cụ thể của bạn.

Phát hiện lưới dường như chỉ áp dụng để phát hiện va chạm giữa các đối tượng chuyển động và nền tĩnh. Ưu điểm lớn nhất của điều này là nền tĩnh được biểu diễn dưới dạng mảng bộ nhớ liền kề và mỗi lần tra cứu va chạm là O (1) với địa phương tốt nếu bạn cần thực hiện nhiều lần đọc (vì các thực thể bao phủ nhiều hơn một ô trong lưới). Nhược điểm, nếu nền tĩnh lớn, là lưới có thể khá lãng phí không gian.

Nếu thay vào đó, bạn biểu thị nền tĩnh là tứ giác, thì chi phí tra cứu riêng lẻ tăng lên, nhưng vì các khối lớn của nền chiếm một lượng không gian nhỏ, nên các yêu cầu bộ nhớ giảm xuống và do đó, nhiều nền có thể nằm trong bộ nhớ cache. ngay cả khi phải mất gấp 10 lần số lần đọc để thực hiện tra cứu theo cấu trúc như vậy, nếu tất cả nằm trong bộ đệm, nó vẫn sẽ nhanh hơn 10 lần so với một lần tra cứu với một lỗi bộ nhớ cache.

Nếu tôi phải đối mặt với sự lựa chọn? Tôi sẽ đi với việc thực hiện lưới, bởi vì nó đơn giản để làm, tốt hơn là dành thời gian của tôi cho các vấn đề khác, thú vị hơn. Nếu tôi nhận thấy rằng trò chơi của tôi chạy chậm một chút, tôi sẽ thực hiện một số hồ sơ và xem những gì có thể sử dụng một số trợ giúp. Nếu có vẻ như trò chơi đang dành nhiều thời gian để phát hiện va chạm, tôi sẽ thử thực hiện một cách khác, như là một phần tư (sau khi hoàn thành tất cả các sửa chữa dễ dàng trước), và tìm hiểu xem điều đó có giúp ích gì không.

Chỉnh sửa: Tôi chưa biết cách phát hiện va chạm lưới liên quan đến việc phát hiện va chạm của nhiều thực thể di động, nhưng thay vào đó, tôi sẽ trả lời cách chỉ số không gian (Quadtree) cải thiện hiệu suất phát hiện so với giải pháp lặp. Giải pháp ngây thơ (và thường hoàn toàn tốt) trông giống như thế này:

foreach actor in actorList:
    foreach target in actorList:
        if (actor != target) and actor.boundingbox intersects target.boundingbox:
            actor.doCollision(target)

Điều này rõ ràng có hiệu suất xung quanh O (n ^ 2), với n số lượng diễn viên hiện đang sống trong trò chơi, bao gồm đạn và tàu vũ trụ và người ngoài hành tinh. Nó cũng có thể bao gồm những trở ngại tĩnh nhỏ.

Điều này hoạt động rất tốt miễn là số lượng các mặt hàng như vậy là khá nhỏ, nhưng bắt đầu trông hơi nghèo khi có hơn vài trăm đối tượng để kiểm tra. 10 đối tượng cho kết quả chỉ trong 100 lần kiểm tra va chạm, 100 kết quả trong 10.000 lần kiểm tra. 1000 kết quả trong một triệu kiểm tra.

Một chỉ số không gian (như tứ giác) có thể liệt kê một cách hiệu quả các vật phẩm mà nó thu thập theo các mối quan hệ hình học. điều này sẽ thay đổi thuật toán va chạm thành một cái gì đó như thế này:

foreach actor in actorList:
    foreach target in actorIndex.neighbors(actor.boundingbox):
       if (actor != target) and actor.boundingbox intersects target.boundingbox:
            actor.doCollision(target)

Hiệu quả của việc này (giả sử phân phối đồng đều các thực thể): thường là O (n ^ 1,5 log (n)), vì chỉ số lấy khoảng so sánh log (n) để so sánh, sẽ có khoảng so sánh sqrt (n) , và có n diễn viên để kiểm tra. Tuy nhiên, trên thực tế, số lượng hàng xóm luôn khá hạn chế, vì nếu xảy ra va chạm, phần lớn thời gian một trong các đối tượng bị xóa hoặc di chuyển khỏi va chạm. do đó bạn chỉ nhận được O (n log (n)). Đối với 10 thực thể, bạn thực hiện (khoảng) 10 so sánh, với 100, bạn làm 200, cho 1000 bạn thực hiện 3000.

Một chỉ mục thực sự thông minh thậm chí có thể kết hợp tìm kiếm hàng xóm với phép lặp số lượng lớn và thực hiện gọi lại trên mỗi thực thể giao nhau. Điều này sẽ cho hiệu suất khoảng O (n), vì chỉ mục được quét một lần thay vì truy vấn n lần.


Tôi không chắc chắn tôi biết những gì bạn đang đề cập đến khi bạn nói "nền tĩnh". Những gì tôi đang đối phó là cơ bản của một game bắn súng 2D, vì vậy nó phát hiện va chạm với tàu không gian và người ngoài hành tinh, đạn và tường.
dotminic

2
Bạn vừa kiếm được huy hiệu "Câu trả lời tuyệt vời" riêng tư của tôi!
Felixyz

Điều này nghe có vẻ ngu ngốc nhưng làm thế nào để tôi thực sự sử dụng tứ giác của mình để chọn đối tượng khác mà đối tượng nên kiểm tra va chạm? Tôi không chắc về cách thức này được thực hiện. Điều này dẫn đến một câu hỏi thứ hai. Giả sử tôi có một đối tượng trong nút không phải là hàng xóm của nút khác, nhưng đối tượng đó đủ lớn để nó kéo dài một vài nút, làm thế nào tôi có thể kiểm tra xung đột thực sự, vì tôi đoán cây có thể coi đó không phải là cây đủ gần để va chạm với các đối tượng trong một nút "ở xa"? Các đối tượng không hoàn toàn phù hợp trong một nút có nên được giữ trong nút cha không?
dotminic

2
Quat-tree vốn là tối ưu phụ cho các tìm kiếm hộp giới hạn chồng chéo. Sự lựa chọn tốt nhất cho điều đó thường là R-Tree. Đối với các cây tứ giác, nếu hầu hết các đối tượng gần giống như điểm, thì có, việc giữ các đối tượng ở các nút bên trong và thực hiện kiểm tra va chạm chính xác trong tìm kiếm hàng xóm mờ. Nếu hầu hết các đối tượng trong chỉ mục là lớn và chồng chéo mà không va chạm, thì một cây tứ giác có lẽ là một lựa chọn kém. Nếu bạn có nhiều câu hỏi kỹ thuật hơn về vấn đề này, bạn nên xem xét đưa chúng đến stackoverflow.com
SingleNegationElimination

Tất cả điều này là khá khó hiểu! Cảm ơn bạn về thông tin.
dotminic

3

Xin lỗi vì đã phục hồi chủ đề cổ nhưng lưới cũ IMHO không được sử dụng thường xuyên cho các trường hợp này. Có rất nhiều lợi thế cho một lưới trong đó việc chèn / loại bỏ tế bào là rất rẻ. Bạn không phải bận tâm đến việc giải phóng một ô vì lưới không có mục đích tối ưu hóa cho các biểu diễn thưa thớt. Tôi nói rằng đã giảm thời gian để chọn marquee, chọn một loạt các phần tử trong một cơ sở mã di sản từ hơn 1200ms xuống còn 20ms bằng cách chỉ thay thế cây tứ giác bằng lưới. Tuy nhiên, công bằng mà nói, cây tứ giác đó thực sự kém, lưu trữ một mảng động riêng biệt trên mỗi nút lá cho các phần tử.

Một cái khác mà tôi thấy cực kỳ hữu ích là các thuật toán rasterization cổ điển của bạn để vẽ các hình dạng có thể được sử dụng để thực hiện tìm kiếm vào lưới. Ví dụ: bạn có thể sử dụng rasterization dòng Bresenham để tìm kiếm các phần tử giao nhau với một dòng, rasterization để tìm những ô nào giao nhau với một đa giác, v.v. Vì tôi làm việc rất nhiều trong xử lý hình ảnh, thật tuyệt khi có thể sử dụng chính xác như vậy mã được tối ưu hóa tôi sử dụng để vẽ các pixel thành một hình ảnh khi tôi sử dụng để phát hiện các giao điểm chống lại các đối tượng chuyển động trong một lưới.

Điều đó nói rằng, để làm cho lưới hiệu quả, bạn không cần nhiều hơn 32 bit cho mỗi ô lưới. Bạn sẽ có thể lưu trữ một triệu tế bào dưới 4 megabyte. Mỗi ô lưới chỉ có thể lập chỉ mục cho phần tử đầu tiên trong ô và phần tử đầu tiên trong ô sau đó có thể lập chỉ mục cho phần tử tiếp theo trong ô. Nếu bạn đang lưu trữ một số loại thùng chứa đầy đủ với mỗi ô duy nhất, sẽ nhanh chóng bùng nổ trong việc sử dụng và phân bổ bộ nhớ. Thay vào đó bạn chỉ có thể làm:

struct Node
{
    int32_t next;
    ...
};

struct Grid
{
     vector<int32_t> cells;
     vector<Node> nodes;
};

Thích như vậy:

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

Được rồi, như vậy đến khuyết điểm. Tôi đang thừa nhận điều này với sự thiên vị và ưu tiên đối với lưới điện, nhưng nhược điểm chính của chúng là chúng không thưa thớt.

Truy cập vào một ô lưới cụ thể được cung cấp tọa độ là thời gian không đổi và không yêu cầu hạ xuống một cây rẻ hơn, nhưng lưới dày đặc, không thưa thớt, do đó bạn có thể phải kiểm tra nhiều ô hơn mức yêu cầu. Trong trường hợp dữ liệu của bạn được phân phối rất thưa thớt, lưới có thể yêu cầu kiểm tra nhiều hơn để tìm ra các yếu tố giao nhau nói một đường hoặc đa giác đầy hoặc hình chữ nhật hoặc đường tròn giới hạn. Lưới phải lưu trữ ô 32 bit đó ngay cả khi nó hoàn toàn trống và khi bạn thực hiện truy vấn giao nhau hình dạng, bạn phải kiểm tra các ô trống đó nếu chúng giao nhau với hình dạng của bạn.

Lợi ích chính của cây tứ giác là khả năng lưu trữ dữ liệu thưa thớt và chỉ chia nhỏ hết mức cần thiết. Điều đó nói rằng, thật khó để thực hiện tốt, đặc biệt là nếu bạn có những thứ di chuyển xung quanh mọi khung hình. Cây cần phân chia và giải phóng các nút con một cách hiệu quả, nếu không, nó biến thành một lưới điện dày đặc lãng phí trên đầu để lưu trữ các liên kết cha-> con. Rất có thể thực hiện một cây tứ giác hiệu quả bằng cách sử dụng các kỹ thuật rất giống với những gì tôi đã mô tả ở trên cho lưới, nhưng nhìn chung sẽ tốn nhiều thời gian hơn. Và nếu bạn làm theo cách tôi làm trong lưới, điều đó cũng không hẳn là tối ưu, vì nó sẽ dẫn đến mất khả năng đảm bảo rằng cả 4 con của một nút bốn cây đều được lưu trữ liên tục.

Ngoài ra, cả cây bốn lá và lưới đều không làm được việc tuyệt vời nếu bạn có một số yếu tố lớn bao trùm toàn bộ khung cảnh, nhưng ít nhất lưới vẫn phẳng và không chia nhỏ đến mức thứ n trong những trường hợp đó . Cây tứ giác nên lưu trữ các yếu tố trong các nhánh và không chỉ để lại xử lý hợp lý các trường hợp như vậy nếu không nó sẽ muốn chia nhỏ như điên và suy giảm chất lượng cực kỳ nhanh chóng. Có nhiều trường hợp bệnh lý như thế này bạn phải chăm sóc với một cây bốn lá nếu bạn muốn nó xử lý phạm vi nội dung rộng nhất. Ví dụ, một trường hợp khác thực sự có thể vấp phải một cây bốn lá là nếu bạn có một khối lượng các yếu tố trùng khớp. Vào thời điểm đó, một số người chỉ dùng đến việc đặt giới hạn độ sâu cho cây bốn lá của họ để ngăn nó phân chia vô hạn. Lưới có một sự hấp dẫn rằng nó làm một công việc tốt,

Tính ổn định và dự đoán cũng có lợi trong bối cảnh trò chơi, vì đôi khi bạn không nhất thiết muốn giải pháp nhanh nhất có thể cho trường hợp thông thường nếu đôi khi có thể dẫn đến trục trặc về tốc độ khung hình trong các tình huống hiếm gặp so với giải pháp khá nhanh xung quanh nhưng không bao giờ dẫn đến những trục trặc như vậy và giữ tốc độ khung hình mượt mà và có thể dự đoán được. Một lưới có loại chất lượng sau này cho nó.

Với tất cả những gì đã nói, tôi thực sự nghĩ rằng nó phụ thuộc vào lập trình viên. Với những thứ như lưới so với quad-tree hoặc octree so với kd-tree so với BVH, phiếu bầu của tôi thuộc về nhà phát triển sung mãn nhất với hồ sơ tạo ra các giải pháp rất hiệu quả cho dù anh ấy / cô ấy sử dụng cấu trúc dữ liệu nào. Có rất nhiều ở cấp độ vi mô, như đa luồng, SIMD, bố cục bộ nhớ thân thiện với bộ nhớ cache và các mẫu truy cập. Một số người có thể xem xét những vi mô đó nhưng họ không nhất thiết phải có tác động vi mô. Những thứ như vậy có thể tạo ra sự khác biệt 100 lần từ giải pháp này sang giải pháp khác. Mặc dù vậy, nếu tôi được cá nhân vài ngày và được cho biết rằng tôi cần triển khai cấu trúc dữ liệu để tăng tốc nhanh chóng phát hiện va chạm của các phần tử di chuyển xung quanh mỗi khung hình, tôi sẽ làm tốt hơn trong thời gian ngắn đó khi thực hiện lưới hơn một phần tư -cây.

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.