Pertama-tama, dalam kasus persegi panjang selaras sumbu, jawaban Kevin Reid adalah yang terbaik dan algoritme adalah yang tercepat.
Kedua, untuk bentuk sederhana, gunakan kecepatan relatif (seperti yang terlihat di bawah) dan teorema sumbu pemisah untuk deteksi tabrakan. Ini akan memberi tahu Anda apakah tabrakan terjadi dalam kasus gerakan linear (tidak ada rotasi). Dan jika ada rotasi, Anda perlu timestep kecil agar lebih akurat. Sekarang, untuk menjawab pertanyaan:
Bagaimana cara memberi tahu dalam kasus umum apakah dua bentuk cembung berpotongan?
Saya akan memberi Anda algoritma yang bekerja untuk semua bentuk cembung dan bukan hanya segi enam.
Misalkan X dan Y adalah dua bentuk cembung. Mereka berpotongan jika dan hanya jika mereka memiliki titik yang sama, yaitu ada titik x ∈ X dan titik y ∈ Y sedemikian rupa sehingga x = y . Jika Anda menganggap spasi sebagai ruang vektor, maka ini sama dengan mengatakan x - y = 0 . Dan sekarang kita sampai ke bisnis Minkowski ini:
The Minkowski sum dari X dan Y adalah himpunan semua x + y untuk x ∈ X dan y ∈ Y .
Contoh untuk X dan Y
X, Y dan jumlah Minkowski mereka, X + Y
Anggaplah (-Y) adalah himpunan semua -y untuk y ∈ Y , maka diberikan paragraf sebelumnya, X dan Y berpotongan jika dan hanya jika X + (-Y) berisi 0 , yaitu asal .
Komentar samping: mengapa saya menulis X + (-Y) alih-alih X - Y ? Nah, karena dalam matematika, ada operasi yang disebut perbedaan Minkowski dari A dan B yang terkadang ditulis X - Y namun tidak ada hubungannya dengan himpunan semua x - y untuk x ∈ X dan y ∈ Y (Minkowski yang asli) perbedaannya sedikit lebih kompleks).
Jadi kami ingin menghitung jumlah Minkowski dari X dan -Y dan untuk menemukan apakah itu berisi asal. Asal tidak khusus dibandingkan dengan titik lain, sehingga untuk menemukan apakah asal berada dalam domain tertentu, kami menggunakan algoritma yang dapat memberi tahu kami apakah ada titik tertentu milik domain itu.
Jumlah Minkowski dari X dan Y memiliki properti keren, yaitu bahwa jika X dan Y cembung, maka X + Y juga. Dan menemukan apakah suatu titik milik himpunan cembung jauh lebih mudah daripada jika himpunan itu tidak (dikenal sebagai) cembung.
Kita tidak mungkin menghitung semua x - y untuk x ∈ X dan y ∈ Y karena ada titik tak terhingga dari titik x dan y , jadi semoga, karena X , Y dan X + Y adalah cembung, kita bisa menggunakan titik "terluar" yang mendefinisikan bentuk X dan Y , yang merupakan simpulnya, dan kita akan mendapatkan titik terluar X + Y , dan juga beberapa lagi.
Poin tambahan ini "dikelilingi" oleh titik terluar X + Y sehingga tidak berkontribusi untuk menentukan bentuk cembung yang baru diperoleh. Kami mengatakan bahwa mereka tidak mendefinisikan " convex hull " dari himpunan poin. Jadi yang kami lakukan adalah menyingkirkannya sebagai persiapan untuk algoritme terakhir yang memberi tahu kami apakah asalnya ada dalam convex hull.
Lambung cembung X + Y. Kami telah menghapus simpul "di dalam".
Karena itu, kami mendapatkannya
Algoritma pertama, naif
boolean intersect(Shape X, Shape Y) {
SetOfVertices minkowski = new SetOfVertices();
for (Vertice x in X) {
for (Vertice y in Y) {
minkowski.addVertice(x-y);
}
}
return contains(convexHull(minkowski), Vector2D(0,0));
}
Loop jelas memiliki kompleksitas O (mn) di mana m dan n adalah jumlah simpul masing-masing bentuk. The minkoswki
set berisi mn elemen paling banyak. The convexHull
algoritma memiliki kompleksitas yang tergantung pada algoritma yang digunakan , dan Anda dapat bertujuan untuk O (k log (k)) di mana k adalah ukuran dari himpunan titik-titik, sehingga dalam kasus kami, kami mendapatkan O (mn log (mn) ) . The contains
algoritma memiliki kompleksitas yang linier dengan jumlah tepi (dalam 2D) atau wajah (dalam 3D) dari lambung cembung, sehingga benar-benar tergantung pada bentuk Anda mulai, tapi tidak akan lebih besar dari O (mn) .
Saya akan membiarkan Anda google untuk contains
algoritma untuk bentuk cembung, itu cukup umum. Saya dapat meletakkannya di sini jika saya punya waktu.
Tapi kami sedang melakukan deteksi tabrakan, jadi kami bisa mengoptimalkannya banyak
Kami awalnya memiliki dua badan A dan B bergerak tanpa rotasi selama timestep dt (dari apa yang saya tahu dengan melihat gambar Anda). Mari kita sebut v A dan v B masing-masing kecepatan A dan B , yang konstan selama catatan waktu durasi dt . Kami mendapatkan yang berikut ini:
dan, seperti yang Anda tunjukkan dalam gambar Anda, badan-badan ini menyapu area (atau volume, dalam 3D) saat bergerak:
dan mereka berakhir sebagai A ' dan B' setelah tanda waktu.
Untuk menerapkan algoritma naif kami di sini, kami hanya perlu menghitung volume sapuan. Tapi kami tidak melakukan ini.
Dalam kerangka referensi B , B tidak bergerak (ya!). Dan A memiliki kecepatan tertentu sehubungan dengan B yang Anda dapatkan dengan menghitung v A - v B (Anda dapat melakukan sebaliknya, menghitung kecepatan relatif B dalam kerangka referensi A ).
Dari kiri ke kanan: kecepatan dalam kerangka referensi dasar; kecepatan relatif; menghitung kecepatan relatif.
Dengan menganggap B sebagai tidak bergerak dalam kerangka acuannya sendiri, Anda hanya perlu menghitung volume yang dilalui A saat bergerak selama dt dengan kecepatan relatifnya v A - v B .
Ini mengurangi jumlah simpul yang akan digunakan dalam perhitungan jumlah Minkowski (kadang-kadang sangat).
Optimalisasi lain yang mungkin adalah pada titik di mana Anda menghitung volume yang disapu oleh salah satu tubuh, katakanlah A. Anda tidak harus menerjemahkan semua simpul yang membentuk A. Hanya yang memiliki tepi (wajah dalam 3D) yang luar "wajah" normal arah penyapuan. Tentunya Anda sudah memperhatikan itu ketika Anda menghitung area sapuan untuk kotak. Anda dapat mengetahui apakah normal menuju arah sapuan menggunakan produk titiknya dengan arah sapuan, yang harus positif.
Optimalisasi terakhir, yang tidak ada hubungannya dengan pertanyaan Anda tentang persimpangan, sangat berguna dalam kasus kami. Ia menggunakan kecepatan relatif yang kami sebutkan dan yang disebut metode sumbu pemisah. Tentunya Anda sudah tahu tentang itu.
Misalkan Anda tahu jari-jari dari A dan B terhadap mereka pusat massa (yang mengatakan, jarak antara pusat massa dan titik terjauh dari itu), seperti ini:
Sebuah tabrakan dapat terjadi hanya jika ada kemungkinan bahwa lingkaran berlari dari A bertemu yang dari B . Kita lihat di sini bahwa itu tidak akan, dan cara untuk memberitahu komputer yang untuk menghitung jarak dari C B ke saya seperti pada gambar berikut dan pastikan itu lebih besar daripada jumlah jari-jari A dan B . Jika lebih besar, tidak ada tabrakan. Jika lebih kecil, maka tabrakan.
Ini tidak bekerja dengan baik dengan bentuk yang agak panjang, tetapi dalam kasus kotak atau bentuk lain, itu adalah heuristik yang sangat baik untuk menyingkirkan tabrakan .
Teorema sumbu pemisah diterapkan ke B dan volume disapu oleh A , akan memberi tahu Anda apakah tabrakan itu terjadi. Kompleksitas dari algoritma yang terkait adalah linier dengan jumlah jumlah simpul dari setiap bentuk cembung, tetapi kurang ajaib ketika tiba saatnya untuk benar-benar menangani tabrakan.
Algoritme baru dan lebih baik kami yang menggunakan persimpangan untuk membantu mendeteksi tabrakan, tetapi masih tidak sebagus teorema sumbu pemisah untuk benar - benar mengetahui apakah tabrakan terjadi
boolean mayCollide(Body A, Body B) {
Vector2D relativeVelocity = A.velocity - B.velocity;
if (radiiHeuristic(A, B, relativeVelocity)) {
return false; // there is a separating axis between them
}
Volume sweptA = sweptVolume(A, relativeVelocity);
return contains(convexHull(minkowskiMinus(sweptA, B)), Vector2D(0,0));
}
boolean radiiHeuristic(A, B, relativeVelocity)) {
// the code here
}
Volume convexHull(SetOfVertices s) {
// the code here
}
boolean contains(Volume v, Vector2D p) {
// the code here
}
SetOfVertices minkowskiMinus(Body X, Body Y) {
SetOfVertices result = new SetOfVertices();
for (Vertice x in X) {
for (Vertice y in Y) {
result.addVertice(x-y);
}
}
return result;
}