Saya memiliki masalah dengan resolusi tabrakan AABB.
Saya menyelesaikan persimpangan AABB dengan menyelesaikan sumbu X terlebih dahulu, lalu sumbu Y. Ini dilakukan untuk mencegah bug ini: http://i.stack.imgur.com/NLg4j.png
Metode saat ini berfungsi dengan baik ketika suatu benda bergerak ke pemain dan pemain harus didorong secara horizontal. Seperti yang dapat Anda lihat di .gif, paku horizontal mendorong pemain dengan benar.
Ketika paku vertikal bergerak ke pemain, bagaimanapun, sumbu X masih diselesaikan terlebih dahulu. Ini membuat "menggunakan paku sebagai tumpangan" tidak mungkin.
Ketika pemain bergerak ke paku vertikal (dipengaruhi oleh gravitasi, jatuh ke dalamnya), dia mendorong sumbu Y, karena tidak ada tumpang tindih pada sumbu X untuk memulai.
Sesuatu yang saya coba adalah metode yang dijelaskan dalam jawaban pertama tautan ini: Deteksi tabrakan objek persegi panjang 2D
Namun paku dan objek bergerak bergerak dengan mengubah posisi, bukan kecepatan, dan saya tidak menghitung posisi prediksi berikutnya hingga metode Pembaruan () mereka dipanggil. Tak perlu dikatakan solusi ini juga tidak berhasil. :(
Saya perlu memecahkan tabrakan AABB dengan cara yang kedua kasus yang dijelaskan di atas berfungsi sebagaimana dimaksud.
Ini adalah kode sumber tabrakan saya saat ini: http://pastebin.com/MiCi3nA1
Saya akan sangat berterima kasih jika seseorang dapat melihat ini, karena bug ini telah ada di mesin sejak awal, dan saya telah berjuang untuk menemukan solusi yang baik, tanpa hasil. Ini serius membuat saya menghabiskan malam melihat kode tabrakan dan mencegah saya untuk sampai ke "bagian yang menyenangkan" dan coding logika permainan :(
Saya mencoba menerapkan sistem tabrakan yang sama seperti pada demo platformer AppHub XNA (dengan menyalin-menempelkan sebagian besar barang). Namun bug "jumping" muncul di gim saya, sementara itu tidak muncul di demo AppHub. [bug melompat: http://i.stack.imgur.com/NLg4j.png ]
Untuk melompat saya memeriksa apakah pemain "onGround", lalu tambahkan -5 ke Velocity.Y.
Karena Velocity.X pemain lebih tinggi dari Velocity.Y (lihat panel keempat dalam diagram), onGround diatur ke true ketika seharusnya tidak, dan dengan demikian memungkinkan pemain melompat di udara.
Saya percaya ini tidak terjadi dalam demo AppHub karena Velocity.X pemain tidak akan pernah lebih tinggi dari Velocity.Y, tapi saya mungkin salah.
Saya memecahkan ini sebelumnya dengan menyelesaikan pada sumbu X terlebih dahulu, kemudian pada sumbu Y. Tapi itu mengacaukan tabrakan dengan paku seperti yang saya sebutkan di atas.
sumber
Jawaban:
OK, saya menemukan mengapa demo platformer XNA AppHub tidak memiliki bug "melompat": demo menguji ubin tabrakan dari atas ke bawah . Ketika berhadapan dengan "dinding" pemain mungkin tumpang tindih beberapa ubin. Urutan resolusi penting karena menyelesaikan satu tabrakan juga dapat menyelesaikan tabrakan lainnya (tetapi dalam arah yang berbeda). The
onGround
properti hanya mengatur kapan tabrakan diselesaikan dengan mendorong pemain pada sumbu y. Resolusi ini tidak akan terjadi jika resolusi sebelumnya mendorong pemain ke bawah dan / atau secara horizontal.Saya dapat mereproduksi bug "melompat" di demo XNA dengan mengubah baris ini:
untuk ini:
(Saya juga mengubah beberapa konstanta yang berhubungan dengan fisika, tetapi ini seharusnya tidak menjadi masalah.)
Mungkin mengurutkan
bodiesToCheck
pada sumbu y sebelum menyelesaikan tabrakan di game Anda akan memperbaiki bug "melompat". Saya menyarankan untuk menyelesaikan tabrakan pada sumbu penetrasi "dangkal", seperti yang ditunjukkan oleh demo XNA dan Trevor . Juga perhatikan pemutar demo XNA dua kali lebih tinggi dari ubin bertabrakan, membuat kasus tabrakan ganda lebih mungkin.sumber
Position = nextPosition
langsung di dalamfor
loop, jika tidak resolusi tabrakan yang tidak diinginkan (pengaturanonGround
) masih terjadi. Pemain harus didorong ke bawah secara vertikal (dan tidak pernah naik) ketika mengenai langit-langit sehinggaonGround
tidak boleh diatur. Ini adalah bagaimana demo XNA melakukannya, dan saya tidak dapat mem-repro bug "plafon" di sana.onGround
diatur, jadi saya tidak bisa menyelidiki mengapa lompatan tidak diizinkan. Demo XNA memperbaruiisOnGround
properti analognya di dalamHandleCollisions()
. Juga, setelah diaturVelocity.Y
ke 0, mengapa gravitasi tidak mulai menggerakkan pemain ke bawah, lagi? Dugaan saya adalahonGround
properti tidak disetel dengan benar. Lihatlah bagaimana demo XNA memperbaruipreviousBottom
danisOnGround
(IsOnGround
).Solusi paling sederhana bagi Anda adalah memeriksa kedua arah tumbukan terhadap setiap objek di dunia sebelum menyelesaikan setiap tumbukan, dan hanya menyelesaikan yang lebih kecil dari dua "tumbukan gabungan" yang dihasilkan. Ini berarti bahwa Anda menyelesaikan dengan jumlah sekecil mungkin, alih-alih selalu menyelesaikan x pertama, atau selalu menyelesaikan y pertama.
Kode Anda akan terlihat seperti ini:
revisi besar : Dari membaca komentar tentang jawaban lain, saya pikir saya akhirnya memperhatikan asumsi yang tidak dinyatakan, yang akan menyebabkan pendekatan ini tidak berfungsi (dan yang menjelaskan mengapa saya tidak dapat memahami masalah yang sebagian - tetapi tidak semua - orang melihat dengan pendekatan ini). Untuk menguraikan, ini ada beberapa pseudocode, yang menunjukkan lebih jelas apa fungsi yang saya rujuk sebelumnya seharusnya lakukan:
Ini bukan tes "per objek pasangan"; itu tidak bekerja dengan menguji dan menyelesaikan objek yang bergerak terhadap setiap ubin peta dunia secara individual (pendekatan itu tidak akan pernah bekerja dengan andal, dan gagal dengan cara yang semakin dahsyat ketika ukuran ubin berkurang). Sebaliknya, ia menguji objek bergerak terhadap setiap objek di peta dunia secara bersamaan, dan kemudian menyelesaikan berdasarkan tabrakan terhadap seluruh peta dunia.
Ini adalah bagaimana Anda memastikan bahwa (misalnya) ubin dinding individu di dalam dinding tidak pernah memantul pemain naik dan turun di antara dua ubin yang berdekatan, mengakibatkan pemain terjebak di beberapa ruang 'antara' mereka yang tidak ada; jarak resolusi tabrakan selalu dihitung sampai ruang kosong di dunia, tidak hanya untuk batas ubin tunggal yang kemudian mungkin memiliki ubin padat lain di atasnya.
sumber
if(fabs( yDistanceToResolve ) < fabs( xDistanceToResolve ) || xDistanceToResolve == 0) { object->Move( 0.f, yDistanceToResolve ); } else { object->Move( xDistanceToResolve, 0.f ); }
Sunting
Saya telah memperbaikinya
Sepertinya hal kunci yang saya ubah adalah mengklasifikasikan persimpangan.
Persimpangan adalah:
dan saya menyelesaikannya dalam urutan itu
Tetapkan tabrakan tanah sebagai pemain setidaknya 1/4 jalan di ubin
Jadi ini adalah tabrakan tanah dan pemain ( biru ) akan duduk di atas ubin ( hitam )
Tapi ini BUKAN tabrakan tanah dan pemain akan "tergelincir" di sisi kanan ubin ketika dia mendarat di atasnya
Dengan metode ini pemain tidak akan lagi terjebak di sisi dinding
sumber
Forenote:
Mesin saya menggunakan kelas deteksi tabrakan yang memeriksa tabrakan dengan semua objek secara real time, hanya ketika suatu objek bergerak, dan hanya di kotak kisi yang saat ini ditempati.
Solusi Saya:
Ketika saya berlari melawan masalah seperti ini di platformer 2d saya, saya melakukan sesuatu seperti berikut:
- (mesin mendeteksi kemungkinan tabrakan, dengan cepat)
- (permainan memberi tahu engine untuk memeriksa tabrakan yang tepat untuk objek tertentu) [mengembalikan vektor objek *]
- (objek melihat kedalaman penetrasi, serta posisi sebelumnya relatif terhadap posisi sebelumnya objek lain, untuk menentukan sisi mana yang akan digeser keluar)
- (objek bergerak, dan rata-rata kecepatannya dengan kecepatan objek (jika objek bergerak))
sumber
Dalam permainan video saya telah memprogram pendekatannya adalah untuk memiliki fungsi yang mengatakan apakah posisi Anda valid, yaitu bool ValidPos (int x, int y, int wi, int he);
Fungsi memeriksa kotak pembatas (x, y, wi, he) terhadap semua geometri dalam game dan mengembalikan false jika ada persimpangan.
Jika Anda ingin bergerak, katakan ke kanan, ambil posisi pemain, tambahkan 4 ke x dan periksa apakah posisi itu valid, jika tidak, periksa dengan + 3, + 2 dll hingga benar.
Jika Anda juga perlu memiliki gravitasi, Anda memerlukan variabel yang tumbuh selama Anda tidak menyentuh tanah (memukul tanah: ValidPos (x, y +1, wi, he) == true, y positif di bawah sini). jika Anda dapat memindahkan jarak itu (mis. ValidPos (x, y + gravity, wi, he) mengembalikan true) Anda jatuh, kadang-kadang berguna ketika Anda seharusnya tidak dapat mengontrol karakter Anda saat jatuh.
Sekarang, masalah Anda adalah bahwa Anda memiliki benda-benda di dunia Anda yang bergerak sehingga pertama-tama Anda perlu memeriksa apakah posisi lama Anda masih valid!
Jika tidak, Anda perlu menemukan posisi itu. Jika objek ingame tidak dapat bergerak lebih cepat daripada mengatakan 2 piksel per revolusi game, Anda perlu memeriksa apakah posisi (x, y-1) valid, lalu (x, y-2) lalu (x + 1, y) dll dll. seluruh ruang antara (x-2, y-2) hingga (x + 2, y + 2) harus diperiksa. Jika tidak ada posisi yang valid maka itu berarti Anda telah 'hancur'.
HTH
Valmond
sumber
Saya punya beberapa pertanyaan sebelum mulai menjawab ini. Pertama, dalam bug asli di mana Anda terjebak di dinding, apakah ubin-ubin di ubin individu kiri bertentangan dengan satu ubin besar? Dan jika ya, apakah pemain terjebak di antara mereka? Jika ya untuk kedua pertanyaan itu, pastikan posisi baru Anda valid . Itu berarti Anda harus memeriksa apakah ada tabrakan di mana Anda memberi tahu pemain untuk pindah. Jadi selesaikan perpindahan minimum seperti yang dijelaskan di bawah ini, dan kemudian gerakkan pemain Anda berdasarkan itu hanya jika ia mampubergerak kesana. Hampir terlalu di bawah hidung: P Ini sebenarnya akan memperkenalkan bug lain, yang saya sebut "kasus sudut". Pada dasarnya dalam hal sudut (seperti kiri bawah di mana paku horisontal keluar di .gif Anda, tetapi jika tidak ada paku) tidak akan menyelesaikan tabrakan, karena akan berpikir bahwa tidak ada resolusi yang Anda hasilkan mengarah ke posisi yang valid . Untuk mengatasi ini, cukup pertahankan apakah tabrakan telah diselesaikan, serta daftar semua resolusi penetrasi minimum. Setelah itu, jika tabrakan belum terselesaikan, lewati setiap resolusi yang Anda hasilkan, dan pantau resolusi X dan Y maksimum maksimum (maksimum tidak harus berasal dari resolusi yang sama). Kemudian atasi tabrakan pada maksimum itu. Ini tampaknya menyelesaikan semua masalah Anda dan juga yang saya temui.
Pertanyaan lain, apakah paku yang Anda tampilkan satu ubin, atau ubin individu? Jika mereka ubin individu yang tipis, Anda mungkin harus menggunakan pendekatan yang berbeda untuk yang horizontal dan vertikal daripada yang saya jelaskan di bawah ini. Tetapi jika semuanya ubin ini harus bekerja.
Baiklah, jadi pada dasarnya inilah yang digambarkan oleh @Trevor Powell. Karena Anda hanya menggunakan AABB, yang harus Anda lakukan adalah menemukan berapa banyak satu persegi panjang menembus yang lain. Ini akan memberi Anda jumlah dalam sumbu X dan Y. Pilih minimum dari keduanya, dan pindahkan objek bertabrakan Anda sepanjang sumbu yang jumlahnya. Hanya itu yang Anda butuhkan untuk menyelesaikan tabrakan AABB. Anda TIDAK AKAN PERNAH perlu bergerak lebih dari satu sumbu dalam tabrakan seperti itu, jadi Anda tidak perlu bingung tentang apa yang harus bergerak terlebih dahulu, karena Anda hanya akan memindahkan minimum.
Perangkat lunak Metanet memiliki tutorial klasik tentang pendekatan di sini . Ini juga masuk ke bentuk lain juga.
Berikut ini adalah fungsi XNA yang saya buat untuk menemukan vektor tumpang tindih dari dua persegi panjang:
(Saya harap ini mudah diikuti, karena saya yakin ada cara yang lebih baik untuk menerapkannya ...)
isCollision (Rectangle) secara harfiah hanya panggilan ke XNA's Rectangle.Intersects (Rectangle).
Saya sudah menguji ini dengan platform bergerak dan sepertinya berfungsi dengan baik. Saya akan melakukan beberapa tes lebih mirip dengan .gif Anda untuk memastikan dan melaporkan kembali jika itu tidak berhasil.
sumber
Itu mudah.
Tulis ulang paku. Ini salah paku.
Tabrakan Anda harus terjadi dalam unit yang terpisah dan nyata. Masalahnya sejauh yang saya bisa lihat adalah:
Masalahnya adalah dengan langkah 2, bukan 3 !!
Jika Anda mencoba membuat sesuatu terasa solid, Anda tidak boleh membiarkan barang-barang meluncur satu sama lain seperti itu. Setelah Anda berada di posisi persimpangan, jika Anda kehilangan tempat, masalahnya menjadi lebih sulit untuk dipecahkan.
Paku idealnya harus memeriksa keberadaan pemain dan, ketika mereka bergerak, mereka harus mendorongnya seperlunya.
Cara mudah untuk mencapai ini adalah agar Player memiliki
moveX
danmoveY
fungsi - fungsi yang memahami lanskap dan akan mendorong pemain dengan delta tertentu atau sejauh yang mereka bisa tanpa mengenai rintangan .Biasanya mereka akan dipanggil oleh loop acara. Namun mereka juga bisa dipanggil oleh objek untuk mendorong pemain.
Jelas pemain masih perlu bereaksi terhadap paku jika dia masuk ke mereka .
Aturan menyeluruh adalah, setelah objek bergerak, itu harus berakhir pada posisi tanpa persimpangan. Dengan cara ini, tidak mungkin untuk mendapatkan gangguan yang Anda lihat.
sumber
moveY
gagal menghapus persimpangan (karena tembok lain menghalangi) Anda dapat memeriksa lagi untuk persimpangan dan coba moveX atau bunuh saja.