Masalah deteksi tabrakan menggunakan AABB

8

Saya menerapkan rutin deteksi tabrakan sederhana menggunakan AABB antara sprite permainan utama saya dan berbagai platform (Silakan lihat kode di bawah). Ini bekerja dengan baik, tetapi saya sekarang memperkenalkan gravitasi untuk membuat karakter saya jatuh dan itu menunjukkan beberapa masalah dengan rutin CD saya.

Saya pikir inti masalahnya adalah bahwa rutin CD saya memindahkan sprite kembali sepanjang sumbu di mana ia telah menembus jumlah paling sedikit ke dalam sprite lainnya. Jadi, jika lebih banyak di sumbu Y daripada X, maka itu akan memindahkannya kembali di sepanjang X Axis.

Namun, sekarang sprite (pahlawan) saya sekarang jatuh pada kecepatan 30 piksel per bingkai (Ini adalah layar tinggi 1504 - saya perlu turun secepat ini karena saya ingin mencoba mensimulasikan gravitasi 'normal', setiap lambat hanya terlihat aneh ) Saya mendapatkan masalah ini. Saya akan mencoba untuk menunjukkan apa yang terjadi (dan apa yang saya pikir menyebabkannya - walaupun saya tidak yakin) dengan beberapa gambar: (Kode di bawah gambar).

Saya akan sangat menghargai beberapa saran tentang cara mengatasi masalah ini.

masukkan deskripsi gambar di sini

Untuk klarifikasi, pada gambar kanan di atas, ketika saya mengatakan posisi dikoreksi 'salah' itu mungkin agak menyesatkan. Kode itu sendiri bekerja dengan benar untuk bagaimana itu ditulis, atau dengan kata lain, algoritma itu sendiri jika berperilaku seperti yang saya harapkan, tapi saya perlu mengubah perilakunya untuk menghentikan masalah ini terjadi, harapan yang mengklarifikasi komentar saya dalam gambar .

masukkan deskripsi gambar di sini

Kode Saya

public boolean heroWithPlatforms(){

    //Set Hero center for this frame
    heroCenterX  = hero.xScreen+(hero.quadWidth/2);
    heroCenterY  = hero.yScreen+(hero.quadHeight/2);

    //Iterate through all platforms to check for collisions
    for(x=0;x<platformCoords.length;x+=2){

    //Set platform Center for this iteration
    platformCenterX = platformCoords[x]+(platforms.quadWidth/2);
    platformCenterY = platformCoords[x+1]+(platforms.quadHeight/2);

    // the Dif variables are the difference (absolute value)
    // of the center of the two sprites being compared (one for X axis difference
    //and on for the Y axis difference)
    difX = Math.abs(heroCenterX-platformCenterX);
    difY = Math.abs(heroCenterY-platformCenterY);

    //If 1
    //Check the difference between the 2 centers and compare it to the vector (the sum of
    //the two sprite's widths/2.  If it is smaller, then the sprites are pverlapping along
    //the X axis and we can now proceed to check the Y axis 
    if (difX<vecXPlatform){

    //If 2
    //Same as above but for the Y axis, if this is also true, then we have a collision
    if(difY<vecYPlatform){
        //we now know sprites are colliding, so we now need to know exactly by how much
        //penX will hold the value equal to the amount one sprite (hero, in this case)
        //has overlapped with the other sprite (the platform)
        penX = vecXPlatform-difX;
        penY = vecYPlatform-difY;
        //One sprite has penetrated into the other, mostly in the Y axis, so move sprite
        //back on the X Axis
        if (penX < penY){hero.xScreen-=penX*(heroCenterX-platformCenterX>=0 ? -1 : 1);}
        //Sprite has penetrated into the other, mostly in the X asis, so move sprite
        //back on the Y Axis
        else if (penY < penX) {hero.yScreen-=penY*(heroCenterY-platformCenterY>=0 ? -1 : 1);}
        return true;
        }//Close 'If' 2
        } //Close 'If' 1
    }
    //Otherwise, no collision

    return false;
    }
BungleBonce
sumber
Untuk menguraikan, mengapa Anda memindahkan sprite kembali pada poros lainnya: //One sprite has penetrated into the other, mostly in the Y axis, so move sprite //back on the X Axis
Tom 'Blue' Piddock
Hai @ Biru, karena sprite harus dikoreksi sepanjang sumbu paling tidak penetrasi, jadi jika sprite telah menembus platform lebih banyak di Sumbu X daripada Y, itu adalah Y yang harus dikoreksi karena lebih rendah dari keduanya.
BungleBonce
kemungkinan duplikat Deteksi Tabrakan, koreksi pemain
Anko
Saya sudah membaca pertanyaan itu sebelum @Anko - itu tidak memberi saya jawaban yang saya butuhkan. Karena itu saya menyusun pertanyaan saya sendiri (Tidak yakin si penanya menanyakan hal yang persis sama, meskipun sulit untuk memastikan) :-)
BungleBonce

Jawaban:

1

selama percobaan saya dengan kanvas HTML5 dan AABB saya menemukan apa yang Anda alami. Ini memang terjadi ketika saya mencoba membuat platform dari kotak yang berdekatan berukuran 32x32 piksel.

Solusi yang saya coba berdasarkan urutan pilihan pribadi saya

1 - Bagi gerakan dengan sumbu

Saya saat ini, dan saya pikir saya akan melanjutkan permainan saya dengannya. Tetapi pastikan untuk memeriksa apa yang saya sebut Phantom AABB jika Anda membutuhkan solusi cepat tanpa harus banyak memodifikasi desain Anda saat ini.

Dunia gim saya memiliki fisika yang sangat sederhana. Tidak ada konsep gaya (belum), hanya perpindahan. Gravitasi adalah perpindahan konstan ke bawah. Belum ada akselerasi.

Perpindahan diterapkan oleh sumbu. Dan dalam hal ini termasuk solusi pertama ini.

Setiap bingkai game:

player.moves.y += gravity;
player.moves.x += move_x;

move_x diatur sesuai dengan pemrosesan input, jika tidak tombol panah ditekan maka move_x = 0. Nilai di bawah nol berarti kiri, jika tidak ke kanan.

Memproses semua sprite bergerak lain dan memutuskan nilai komponen "bergerak" mereka, mereka juga memiliki komponen "bergerak".

Catatan: setelah deteksi dan respons tabrakan, komponen pemindahan harus diatur ke nol, baik properti x dan y, karena centang selanjutnya gravitasi akan ditambahkan lagi. Jika Anda tidak mengatur ulang, Anda akan mengakhiri dengan gerakan. Dua kali gravitasi yang diinginkan, dan pada centang berikutnya akan tiga kali.

Lalu masukkan kode penempatan dan deteksi tabrakan.

Komponen pemindahan tidak benar-benar posisi sprite saat ini. Sprite belum bergerak bahkan jika move.x atau y berubah. Komponen "move" adalah cara untuk menghemat perpindahan yang akan diterapkan ke posisi sprite di bagian loop permainan yang benar.

Proses dulu sumbu y (move.y). Terapkan move.y ke sprite.position.y. Kemudian terapkan metode AABB untuk melihat apakah sprite bergerak yang sedang diproses tumpang tindih dengan platform AABB (Anda sudah mengerti bagaimana melakukan ini). Jika tumpang tindih maka pindahkan sprite kembali pada sumbu y dengan menerapkan penetrasi. Y. Ya, berlawanan dengan jawaban saya yang lain, sekarang metode evolved ini mengabaikan penetrasi. X, untuk langkah proses ini. Dan kami menggunakan penetrasi. Bahkan walaupun itu lebih besar dari penetrasi. X, kami bahkan tidak memeriksanya.

Kemudian proses sumbu x (move.x). Terapkan bergerak.x ke sprite.position.x. Dan lakukan yang sebaliknya dari sebelumnya. Anda akan memindahkan sprite hanya pada sumbu horizontal kali ini dengan menerapkan penetrasi.x. Seperti sebelumnya, Anda tidak peduli apakah penetrasi.x kurang atau lebih besar dari penetrasi.

Konsep di sini, adalah bahwa jika sprite tidak bergerak, dan pada awalnya tidak bertabrakan, maka akan tetap seperti itu di frame berikutnya (sprite.moves.x dan y adalah nol). Kasus khusus di mana objek permainan lain secara ajaib berpindah ke posisi yang tumpang tindih dengan sprite mulai diproses adalah sesuatu yang akan saya tangani nanti.

Ketika sprite mulai bergerak. Jika hanya bergerak di sumbu x, maka kami hanya tertarik jika menembus sesuatu di sebelah kiri atau kanan. Karena sprite tidak bergerak ke bawah, dan kami tahu ini karena properti y dari komponen pemindahan adalah nol, kami bahkan tidak memeriksa nilai yang dihitung untuk penetrasi. Jika penetrasi ada dalam sumbu gerakan, kami menerapkan koreksi, jika tidak kami hanya membiarkan sprite bergerak.

Bagaimana dengan perpindahan diagonal, tidak harus dalam sudut 45 derajat?

Sistem yang diusulkan dapat menanganinya. Anda hanya perlu memproses setiap sumbu secara terpisah. Selama simulasi Anda. Kekuatan yang berbeda diterapkan pada sprite. Bahkan jika mesin Anda belum memahami kekuatan. Gaya-gaya ini diterjemahkan menjadi perpindahan sewenang-wenang dalam bidang 2D, perpindahan ini adalah vektor 2D ke segala arah dan dengan panjang apa pun. Dari vektor ini Anda dapat mengekstrak sumbu y dan sumbu x.

Apa yang harus dilakukan jika vektor perpindahan terlalu besar?

Anda tidak menginginkan ini:

|
|
|
|
|
|
|
|_ _ _ _ _ _ _ _ _

Saat langkah baru kami menerapkan proses logika, sumbu pertama dan kemudian yang lain, perpindahan besar mungkin berakhir dilihat oleh kode tabrakan seperti L besar, ini tidak akan mendeteksi dengan benar tabrakan dengan objek yang memotong vektor perpindahan Anda.

Solusinya adalah membagi perpindahan besar dalam langkah-langkah kecil, idealnya lebar atau tinggi sprite Anda, atau setengah lebar atau tinggi sprite Anda. Untuk mendapatkan sesuatu seperti ini:

|_
  |_
    |_
      |_
        |_
          |_
            |_
              |_

Bagaimana dengan teleporting sprite?

Jika Anda menyatakan teleportasi sebagai perpindahan besar, menggunakan komponen gerakan yang sama dengan gerakan normal, maka tidak ada masalah. Jika tidak, maka Anda harus memikirkan cara untuk menandai sprite teleportasi untuk diproses oleh kode tumbukan (menambahkan ke daftar yang didedikasikan untuk tujuan ini?), Selama kode respons Anda dapat langsung memindahkan sprite berdiri yang ada di cara ketika teleportasi terjadi.

2 - Phantom AABB

Memiliki AABB sekunder untuk setiap sprite yang dipengaruhi oleh gravitasi yang dimulai dari dasar sprite, memiliki lebar spab AABB yang sama, dan tinggi 1 piksel.

Idenya di sini adalah bahwa AABB hantu ini selalu bertabrakan dengan platform di bawah sprite. Hanya ketika AABB phantom ini tidak tumpang tindih, gravitasi apa pun diizinkan untuk diterapkan pada sprite.

Anda tidak perlu menghitung vektor penetrasi untuk phabom AABB dan platform. Anda hanya tertarik pada uji tabrakan sederhana, jika tumpang tindih platform, gravitasi tidak dapat diterapkan. Sprite Anda dapat memiliki properti boolean yang memberi tahu apakah itu lebih dari platform atau di udara. Anda berada di udara ketika hantu AABB Anda tidak bertabrakan dengan platform di bawah pemain.

Ini:

player.position.y += gravity;

Menjadi sesuatu seperti ini:

if (player.onGround == false) player.position.y += gravity;

Sangat sederhana dan andal.

Anda membiarkan implementasi AABB Anda apa adanya.

Hantu AABB mungkin memiliki loop khusus yang memprosesnya, yang hanya memeriksa tabrakan sederhana, dan jangan buang waktu menghitung vektor penetrasi. Pengulangan ini harus sebelum kode tumbukan dan respons normal. Anda dapat menerapkan gravitasi selama loop ini, untuk sprite di udara menerapkan gravitasi. Jika phantom AABB itu sendiri adalah kelas atau struktur, itu mungkin memiliki referensi ke sprite miliknya.

Ini sangat mirip dengan solusi yang diusulkan lainnya di mana Anda memeriksa apakah pemain Anda melompat. Saya memberikan cara yang mungkin untuk mendeteksi jika pemain memiliki kaki di atas platform.

Hatoru Hansou
sumber
Saya suka solusi Phantom AABB di sini. Saya sudah menerapkan sesuatu yang mirip untuk mengatasi masalah. (Tapi tidak menggunakan hantu AABB) - Saya tertarik untuk mempelajari lebih lanjut tentang metode awal Anda di atas, tapi saya agak bingung karenanya. Apakah komponen Moves.x (dan .y) pada dasarnya adalah jumlah yang akan dipindahkan oleh sprite (jika diizinkan untuk dipindahkan)? Juga, saya tidak jelas bagaimana ini akan membantu situasi saya secara langsung. Akan sangat berterima kasih jika Anda dapat mengedit jawaban Anda untuk menunjukkan grafik bagaimana itu bisa membantu situasi saya (seperti pada gambar saya di atas) - terima kasih!
BungleBonce
Tentang komponen pemindahan. Ini mewakili perpindahan di masa depan, ya, tetapi Anda tidak memeriksa apakah Anda mengizinkan gerakan atau tidak, Anda benar-benar menerapkan gerakan, dengan sumbu, y pertama, lalu x, dan kemudian melihat apakah Anda bertabrakan dengan sesuatu, jika Anda menggunakan penetrasi vektor untuk mundur. Mengapa menyimpan perpindahan untuk pemrosesan nanti berbeda dari benar-benar menerapkan perpindahan ketika ada input pengguna untuk diproses atau sesuatu terjadi di dunia game? Saya akan mencoba memasukkan ini ke dalam gambar ketika saya mengedit jawaban saya. Sementara itu mari kita lihat apakah orang lain datang dengan alternatif.
Hatoru Hansou
Ah OK - saya pikir saya mengerti apa yang Anda katakan sekarang. Jadi, alih-alih memindahkan x & y dan kemudian memeriksa tumbukan, dengan metode Anda, Anda pindahkan X terlebih dahulu, lalu periksa tumbukan, jika bertabrakan maka perbaiki sepanjang sumbu X. Kemudian gerakkan Y dan periksa tabrakan, jika ada tabrakan, lalu bergerak sepanjang X-Axis? Dengan cara ini kita selalu tahu sumbu dampak yang benar? Apakah saya benar? Terima kasih!
BungleBonce
Peringatan: Anda menulis "Lalu pindahkan Y dan periksa tabrakan, jika ada tabrakan, lalu bergerak di sepanjang X-Axis?" Sumbu Y-nya. Mungkin salah ketik. Ya, itulah pendekatan saya saat ini. Untuk perpindahan yang sangat besar, lebih dari lebar atau tinggi sprite Anda, Anda perlu membagi dalam langkah-langkah yang lebih kecil, dan itulah yang saya maksud dengan analogi L. Berhati-hatilah untuk tidak menerapkan perpindahan besar pada sumbu y dan kemudian x besar, Anda harus membagi keduanya dalam langkah-langkah kecil dan interkalasi untuk mendapatkan perilaku yang benar. Sesuatu yang perlu diperhatikan hanya jika perpindahan besar diizinkan dalam gim Anda. Di tangan saya, saya tahu itu akan terjadi. Jadi saya bersiap untuk itu.
Hatoru Hansou
Brilliant @HatoruHansou, saya tidak pernah berpikir untuk melakukan hal-hal seperti ini, saya pasti akan mencobanya - terima kasih banyak atas bantuan Anda, saya akan menandai jawaban Anda sebagai diterima. :-)
BungleBonce
5

Saya akan menggabungkan ubin platform ke dalam entitas platform tunggal.

Misalnya, Anda mengambil 3 ubin dari gambar dan menggabungkannya menjadi satu kesatuan, entitas Anda akan memiliki kotak tabrakan:

Left   = box1.left
Right  = box3.right
Top    = box1.top
Bottom = box1.bottom

Menggunakan itu untuk tabrakan akan memberi Anda y sebagai sumbu penetrasi paling sedikit di sebagian besar platform sambil tetap memungkinkan Anda untuk memiliki ubin khusus di dalam platform.

Garis BawahZero
sumber
Hai @UnderscoreZero - terima kasih, ini adalah salah satu cara untuk pergi, satu-satunya hal yang saat ini saya simpan ubin saya di 3 array terpisah, satu untuk ubin tepi kiri, untuk ubin tengah dan satu untuk ubin tepi kanan - saya kira saya bisa menggabungkan semuanya menjadi satu array untuk keperluan CD, hanya saja tidak yakin bagaimana saya bisa menulis kode untuk melakukan itu.
BungleBonce
Pilihan lain yang bisa Anda coba yang banyak digunakan mesin fisika adalah begitu objek telah mendarat di bidang datar, mereka menonaktifkan gravitasi untuk objek agar tidak mencoba jatuh. Setelah objek menerima kekuatan yang cukup untuk menariknya menjauh dari pesawat atau jatuh dari sisi pesawat, mereka mengaktifkan kembali gravitasi dan melakukan fisika seperti biasa.
UnderscoreZero
0

Masalah seperti ini biasa terjadi dengan metode pendeteksian tabrakan baru.

Saya tidak tahu terlalu banyak tentang pengaturan tabrakan Anda saat ini, tetapi begini caranya:

Pertama, buat variabel-variabel ini:

  1. Buat kecepatan X dan Y
  2. Buat Gravitasi
  3. Buat jumpSpeed
  4. Buat isjumping

Kedua, buat penghitung waktu untuk menghitung delta untuk memengaruhi kecepatan objek.

Ketiga, lakukan ini:

  1. Periksa apakah pemain menekan tombol melompat dan tidak melompat, jika demikian tambahkan jumpSpeed ​​ke kecepatan Y.
  2. Kurangi gravitasi dari kecepatan Y setiap pembaruan untuk mensimulasikan gravitasi
  3. Jika tinggi pemain + y lebih kecil dari atau sama dengan y platform, atur kecepatan Y ke 0 dan masukkan Y pemain ke platform y + tinggi pemain.

Jika Anda melakukan ini, seharusnya tidak ada masalah. Semoga ini membantu!

LiquidFeline
sumber
Hai @ user1870398, terima kasih, tapi gim saya bahkan tidak melompat pada saat ini, pertanyaan yang berkaitan dengan masalah yang muncul melalui penggunaan metode 'poros paling tidak menembus' untuk memperbaiki posisi sprite. Masalahnya adalah gravitasi menarik karakter saya lebih jauh ke platform saya di sepanjang Sumbu Y daripada tumpang tindih pada Sumbu X, jadi rutinitas saya (seperti yang benar untuk jenis algoritma ini) memperbaiki sumbu X, jadi saya tidak bisa bergerak di sepanjang platform (seperti rutin CD melihat ubin individu dan memperlakukan mereka seperti itu meskipun mereka disajikan sebagai satu platform).
BungleBonce
Saya mendapatkan masalah Anda. Hanya saja Anda tidak benar memperbaiki gerakan Anda. Anda memerlukan variabel isJumping . Jika Anda menekan tombol lompat dan tidak melompat, Anda menyetelnya ke true, jika Anda menekan tanah, setel ke false, pindahkan Y pemain ke posisi yang benar ( platform.y + player.height ), dan atur isJumping ke false. Selanjutnya, tepat setelah memeriksa hal-hal itu, lakukan jika (left_key) player.velocity.x - = (player.isJumping? Player.speed: player.speed - player.groundFriction) . Saya tahu Anda belum menerapkan jumping, jadi sederhananya isJumping disetel ke false.
LiquidFeline
Saya tidak memiliki lompatan tetapi jika variabel 'ifJumping' selalu disetel ke false, bagaimana ini berbeda dengan tidak memilikinya? (katakanlah dalam permainan di mana tidak ada lompatan) Saya hanya sedikit bingung bagaimana boolean 'isjumping' dapat membantu dalam situasi saya - akan berterima kasih jika Anda bisa menjelaskan sedikit lebih banyak - terima kasih!
BungleBonce
Itu tidak diperlukan. Hanya lebih baik untuk mengimplementasikannya saat Anda masih memprogramnya. Bagaimanapun, alasan isJumping adalah untuk mengatur kode Anda, dan mencegah masalah dengan hanya menambahkan gravitasi saat pemain melompat. Lakukan saja apa yang saya rekomendasikan. Metode ini yang digunakan kebanyakan orang. Bahkan Minecraft menggunakannya! Ini adalah bagaimana gravitasi disimulasikan secara efektif! :)
LiquidFeline