Quad tree vs Grid collision detection


27

Saya membuat game tipe co-op 4 pemain, dan saya akan mengimplementasikan kode deteksi tabrakan. Saya telah membaca banyak artikel dan hal-hal tentang cara menangani deteksi tabrakan, tetapi saya mengalami kesulitan mencari tahu apa yang harus dilakukan. Tampaknya pohon quad adalah cara yang paling umum, tetapi dalam beberapa sumber mereka menyebutkan solusi berbasis grid. Karena telah menggunakan kotak untuk deteksi pada game sebelumnya, saya nyaman dengan itu, tetapi apakah sebenarnya lebih baik daripada quad tree? Saya tidak yakin yang menawarkan kinerja terbaik, dan saya juga menjalankan sedikit patokan, tidak ada banyak perbedaan antara kedua solusi.

Apakah yang satu lebih baik dari yang lain? atau lebih elegan? Saya benar-benar tidak yakin yang mana yang harus saya gunakan.

Semua saran dipersilahkan. Terima kasih.

Jawaban:


31

Jawaban yang tepat tergantung sedikit pada permainan yang sebenarnya Anda rancang, dan memilih satu di antara yang lain benar-benar akan membutuhkan penerapan keduanya dan melakukan profil untuk mengetahui mana yang lebih efisien waktu atau ruang pada gim spesifik Anda.

Deteksi kisi tampaknya hanya berlaku untuk mendeteksi tabrakan antara objek bergerak dan latar belakang statis. Keuntungan terbesar untuk ini adalah bahwa latar belakang statis direpresentasikan sebagai susunan memori yang berdekatan, dan setiap pencarian tabrakan adalah O (1) dengan lokalitas yang baik jika Anda perlu melakukan banyak pembacaan (karena entitas mencakup lebih dari satu sel dalam grid). Kerugiannya, jika latar belakang statisnya besar, adalah bahwa kisi-kisi bisa agak boros ruang.

Jika sebaliknya Anda mewakili latar belakang statis sebagai quadtree, maka biaya pencarian individu naik, tetapi karena blok besar latar belakang memakan sedikit ruang, persyaratan memori turun, dan lebih banyak latar belakang dapat duduk di cache. bahkan jika dibutuhkan 10 kali lebih banyak membaca untuk melakukan pencarian dalam struktur seperti itu, jika semuanya ada dalam cache, itu akan tetap 10 kali lebih cepat daripada pencarian tunggal dengan cache miss.

Jika saya dihadapkan dengan pilihan? Saya akan pergi dengan implementasi grid, karena itu bodoh untuk dilakukan, lebih baik menghabiskan waktu saya pada masalah lain yang lebih menarik. Jika saya perhatikan bahwa permainan saya berjalan agak lambat, saya akan melakukan beberapa profil dan melihat apa yang bisa menggunakan bantuan. Jika sepertinya game menghabiskan banyak waktu melakukan deteksi tabrakan, saya akan mencoba implementasi lain, seperti quadtree (setelah menghabiskan semua perbaikan mudah terlebih dahulu), dan mencari tahu apakah itu membantu.

Sunting: Saya belum mendapatkan petunjuk bagaimana pendeteksian tabrakan kisi berhubungan dengan pendeteksian tabrakan beberapa entitas seluler, tetapi sebaliknya, saya akan menjawab bagaimana indeks spasial (Quadtree) meningkatkan kinerja pendeteksian melalui solusi berulang. Solusi naif (dan biasanya sangat baik-baik saja) terlihat seperti ini:

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

Ini jelas memiliki kinerja sekitar O (n ^ 2), dengan jumlah aktor yang saat ini hidup dalam permainan, termasuk peluru dan pesawat ruang angkasa dan alien. Itu juga dapat mencakup hambatan statis kecil.

Ini bekerja dengan sangat baik selama jumlah item tersebut cukup kecil, tetapi mulai terlihat sedikit buruk ketika ada lebih dari beberapa ratus objek yang perlu diperiksa. 10 objek menghasilkan hanya 100 cek benturan, 100 hasil dalam 10.000 cek. 1000 hasil dalam satu juta cek.

Indeks spasial (seperti quadtrees) dapat secara efisien menyebutkan item yang dikumpulkan sesuai dengan hubungan geometris. ini akan mengubah algoritme tabrakan menjadi seperti ini:

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

Efisiensi ini (dengan asumsi distribusi entitas yang seragam): biasanya O (n ^ 1,5 log (n)), karena indeks membutuhkan sekitar log (n) perbandingan untuk dilintasi, akan ada sekitar sqrt (n) tetangga untuk membandingkan , dan ada n aktor untuk diperiksa. Namun, secara realistis, jumlah tetangga selalu sangat terbatas, karena jika tabrakan terjadi, sebagian besar waktu salah satu objek dihapus, atau menjauh dari tabrakan. dengan demikian Anda mendapatkan hanya O (n log (n)). Untuk 10 entitas, Anda melakukan 10 perbandingan, untuk 100, Anda melakukan 200, untuk 1000 Anda melakukan 3000.

Indeks yang sangat pintar bahkan dapat menggabungkan pencarian tetangga dengan iterasi massal, dan melakukan panggilan balik pada setiap entitas yang berpotongan. Ini akan memberikan kinerja sekitar O (n), karena indeks sedang dipindai sekali daripada ditanya n kali.


Saya tidak yakin saya tahu apa yang Anda maksud ketika Anda mengatakan "latar belakang statis". Apa yang saya hadapi pada dasarnya adalah penembak 2D, jadi deteksi tabrakan dengan kapal ruang angkasa dan alien, peluru dan dinding.
dotminic

2
Anda baru saja mendapatkan lencana "Jawaban Hebat" pribadi saya!
Felixyz

Ini mungkin terdengar bodoh, tetapi bagaimana saya benar-benar menggunakan quadtree saya untuk memilih terhadap objek lain mana yang harus diuji tabrakan? Saya tidak yakin bagaimana ini dilakukan. Yang memunculkan pertanyaan kedua. Katakanlah saya memiliki objek dalam node yang bukan tetangga dari node lain, tetapi bahwa objek tersebut cukup besar sehingga membentang beberapa node, bagaimana saya bisa mengecek tabrakan yang sebenarnya, karena saya menduga pohon mungkin menganggap itu bukan cukup dekat untuk bertabrakan dengan benda-benda di simpul "jauh"? Haruskah benda-benda yang tidak sepenuhnya cocok dalam suatu simpul disimpan di simpul induk?
dotminic

2
Quat-tree pada dasarnya sub-optimal untuk pencarian kotak bounding yang tumpang tindih. Pilihan terbaik untuk itu biasanya R-Tree. Untuk quad-tree, jika sebagian besar objek kira-kira seperti titik, maka ya, masuk akal untuk menjaga objek di node batin, dan melakukan pengujian tabrakan yang tepat pada pencarian tetangga fuzzy. Jika sebagian besar objek dalam indeks besar dan tumpang tindih tanpa bertabrakan, Pohon quad mungkin adalah pilihan yang buruk. Jika Anda memiliki pertanyaan teknis lebih lanjut tentang ini, Anda harus mempertimbangkan membawanya ke stackoverflow.com
SingleNegationElimination

Semua ini cukup membingungkan! terimakasih atas infonya.
dotminic

3

Maaf untuk menghidupkan kembali utas kuno tapi kisi-kisi lama IMHO sederhana tidak cukup sering digunakan untuk kasus ini. Ada banyak keuntungan untuk grid dalam penyisipan / penghapusan sel adalah tanah murah. Anda tidak perlu repot membebaskan sel karena kisi tidak memiliki tujuan untuk mengoptimalkan representasi yang jarang. Saya mengatakan bahwa telah mengurangi waktu untuk tenda memilih sekelompok elemen dalam basis kode warisan dari lebih dari 1200 ms ke 20 ms dengan hanya mengganti quad-tree dengan grid. Namun dalam keadilan, quad-tree itu benar-benar diimplementasikan dengan buruk, menyimpan array dinamis yang terpisah per node daun untuk elemen.

Yang lain yang saya temukan sangat berguna adalah bahwa algoritma rasterisasi klasik Anda untuk menggambar bentuk dapat digunakan untuk melakukan pencarian ke dalam kisi. Misalnya, Anda dapat menggunakan rasterisasi garis Bresenham untuk mencari elemen yang memotong garis, memindai garis rasterisasi untuk menemukan sel apa yang memotong poligon, dll. Karena saya banyak bekerja dalam pemrosesan gambar, sungguh menyenangkan bisa menggunakan yang sama persis kode yang dioptimalkan saya gunakan untuk plot piksel ke gambar seperti yang saya gunakan untuk mendeteksi persimpangan terhadap objek yang bergerak dalam kotak.

Yang mengatakan, untuk membuat grid efisien, Anda tidak perlu lebih dari 32-bit per sel grid. Anda harus dapat menyimpan sejuta sel di bawah 4 megabita. Setiap sel kotak hanya dapat mengindeks elemen pertama dalam sel, dan elemen pertama dalam sel kemudian dapat mengindeks elemen berikutnya dalam sel. Jika Anda menyimpan semacam wadah penuh dengan setiap sel tunggal, yang meledak dalam penggunaan memori dan alokasi dengan cepat. Sebagai gantinya Anda hanya dapat melakukan:

struct Node
{
    int32_t next;
    ...
};

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

Seperti itu:

masukkan deskripsi gambar di sini

Oke, begitu seterusnya ke kontra. Saya mengakui ini dengan bias dan preferensi terhadap grid, tetapi kelemahan utama mereka adalah bahwa mereka tidak jarang.

Mengakses sel kisi tertentu yang diberikan koordinat adalah waktu yang konstan dan tidak mengharuskan turun pohon yang lebih murah, tetapi kisi tersebut padat, tidak jarang, sehingga Anda akhirnya harus memeriksa lebih banyak sel daripada yang diperlukan. Dalam situasi di mana data Anda sangat jarang terdistribusi, kisi-kisi tersebut mungkin memerlukan cara pemeriksaan lebih banyak untuk mengetahui elemen-elemen yang bersinggungan misalnya garis atau poligon yang terisi atau persegi panjang atau lingkaran pembatas. Kotak harus menyimpan sel 32-bit itu meskipun itu benar-benar kosong, dan ketika Anda melakukan kueri persimpangan bentuk, Anda harus memeriksa sel-sel kosong itu jika mereka memotong bentuk Anda.

Manfaat utama quad-tree adalah kemampuannya untuk menyimpan data yang jarang dan hanya membagi sebanyak yang diperlukan. Yang mengatakan, lebih sulit untuk menerapkan dengan sangat baik, terutama jika Anda memiliki hal-hal yang bergerak di setiap frame. Pohon perlu membagi dan membebaskan node anak dengan cepat secara efisien, jika tidak maka degradasi menjadi grid padat yang memboroskan biaya overhead untuk menyimpan hubungan anak-anak. Sangat mungkin untuk mengimplementasikan quad-tree yang efisien menggunakan teknik yang sangat mirip dengan apa yang saya jelaskan di atas untuk grid, tetapi umumnya akan lebih intensif waktu. Dan jika Anda melakukannya dengan cara yang saya lakukan di grid, itu belum tentu optimal juga, karena itu akan menyebabkan hilangnya kemampuan untuk menjamin bahwa semua 4 anak-anak dari simpul quad-tree disimpan secara berdekatan.

Juga quad-tree dan grid tidak melakukan pekerjaan luar biasa jika Anda memiliki sejumlah elemen besar yang menjangkau sebagian besar seluruh adegan, tetapi setidaknya grid tetap datar dan tidak dibagi ke tingkat ke-n dalam kasus-kasus tersebut. . Quad-tree harus menyimpan elemen di cabang-cabang dan tidak hanya pergi untuk menangani kasus-kasus seperti itu atau kalau tidak, ia akan ingin membagi seperti gila dan menurunkan kualitas dengan sangat cepat. Ada lebih banyak kasus patologis seperti ini yang harus Anda atasi dengan quad-tree jika Anda ingin menangani rentang konten terluas. Sebagai contoh, case lain yang benar-benar dapat naik quad-tree adalah jika Anda memiliki muatan kapal elemen kebetulan. Pada saat itu beberapa orang hanya menggunakan pengaturan batas kedalaman untuk quad-tree mereka untuk mencegahnya dibagi lagi secara tak terbatas. Grid memiliki daya tarik bahwa ia melakukan pekerjaan yang layak,

Stabilitas dan prediktabilitas juga bermanfaat dalam konteks permainan, karena kadang-kadang Anda tidak selalu menginginkan solusi tercepat untuk kasus umum jika kadang-kadang dapat menyebabkan cegukan dalam laju bingkai dalam skenario kasus langka versus solusi yang cukup cepat semuanya- sekitar tetapi tidak pernah mengarah ke cegukan seperti itu dan membuat frame rate mulus dan dapat diprediksi. Sebuah kisi memiliki kualitas seperti itu.

Dengan semua yang dikatakan, saya benar-benar berpikir itu tergantung pada programmer. Dengan hal-hal seperti kisi vs quad-tree atau octree vs kd-tree vs BVH, suara saya ada di pengembang paling produktif dengan catatan untuk menciptakan solusi yang sangat efisien tidak peduli struktur data apa yang dia gunakan. Ada banyak juga di tingkat mikro, seperti multithreading, SIMD, layout memori yang ramah cache dan pola akses. Beberapa orang mungkin menganggap mikro itu tetapi tidak memiliki dampak mikro. Hal-hal seperti itu dapat membuat perbedaan 100x dari satu solusi ke solusi lainnya. Terlepas dari ini, jika saya secara pribadi diberikan beberapa hari dan diberi tahu bahwa saya perlu menerapkan struktur data untuk secara cepat mempercepat deteksi tabrakan elemen yang bergerak di sekitar setiap frame, saya akan melakukan lebih baik dalam waktu yang singkat mengimplementasikan sebuah grid daripada quad -pohon.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.