Bagaimana menangani deteksi tabrakan pixel-sempurna dengan rotasi?

8

Apakah ada yang punya ide bagaimana cara mencapai deteksi tabrakan pixel-sempurna rotasi dengan Bitmap di Android? Atau secara umum dalam hal ini? Saya memiliki array piksel saat ini tetapi saya tidak tahu bagaimana cara memanipulasi mereka berdasarkan jumlah derajat yang berubah-ubah.

KERING
sumber

Jawaban:

11

Saya tidak terbiasa dengan Android jadi saya tidak tahu alat apa yang Anda miliki, tetapi saya dapat memberi tahu Anda cara untuk menerapkannya secara umum. Betapa mudahnya itu tergantung pada apa yang disediakan Android untuk Anda. Anda akan membutuhkan matriks atau setidaknya mereka akan menyederhanakan banyak perhitungan.

Sebagai permulaan, lakukan tabrakan kotak pembatas dan segera kembali jika mereka tidak bertabrakan untuk menghindari perhitungan lebih lanjut. Itu logis karena jika kotak pembatas tidak bertabrakan, dijamin tidak ada piksel yang akan bertabrakan juga.

Setelah itu, jika diperlukan pemeriksaan tabrakan piksel sempurna, maka poin terpenting adalah Anda harus melakukan pemeriksaan itu di ruang yang sama . Ini dapat dilakukan dengan mengambil setiap piksel dari sprite A, menerapkan serangkaian transformasi untuk membawanya ke ruang lokal sprite B, dan kemudian memeriksa apakah itu bertabrakan dengan piksel apa pun pada posisi tersebut pada sprite B. Tumbukan terjadi ketika kedua piksel diperiksa buram.

Jadi, hal pertama yang Anda butuhkan adalah membuat matriks dunia untuk masing-masing sprite. Mungkin ada tutorial online yang mengajarkan Anda cara membuatnya, tetapi pada dasarnya harus merupakan gabungan dari beberapa matriks sederhana dengan urutan sebagai berikut:

Translation(-Origin) * Scale * Rotation * Translation(Position)

Kegunaan dari matriks ini adalah dengan mengalikan titik di ruang lokal - dan misalnya jika Anda mendapatkan piksel menggunakan metode seperti bitmap.getPixelAt(10,20)10,20 didefinisikan dalam ruang lokal - dengan matriks dunia yang sesuai akan memindahkannya ke ruang dunia:

LocalA * WorldMatrixA -> World
LocalB * WorldMatrixB -> World

Dan jika Anda membalikkan matriks Anda juga dapat pergi ke arah yang berlawanan yaitu mengubah titik dari ruang dunia ke masing-masing ruang lokal sprite tergantung pada matriks yang Anda gunakan:

World * InverseWorldMatrixA -> LocalA
World * InverseWorldMatrixB -> LocalB

Jadi untuk memindahkan titik dari ruang lokal sprite A ke ruang lokal sprite B , Anda pertama-tama mengubahnya dengan menggunakan matriks dunia sprite A, untuk membawanya ke ruang dunia, dan kemudian menggunakan matriks dunia terbalik sprite B , untuk memasukkannya ke dalam ruang lokal sprite B:

LocalA * WorldMatrixA -> World * InverseWorldMatrixB -> LocalB

Setelah transformasi, Anda memeriksa apakah titik baru berada dalam batas sprite B, dan jika ya, Anda memeriksa piksel di lokasi tersebut seperti yang Anda lakukan untuk sprite A. Jadi seluruh proses menjadi seperti ini (dalam kodesemu dan tidak teruji) :

bool PixelCollision(Sprite a, Sprite B)
{
    // Go over each pixel in A
    for(i=0; i<a.Width; ++i)
    {
        for(j=0; j<a.Height; ++j)
        {
            // Check if pixel is solid in sprite A
            bool solidA = a.getPixelAt(i,j).Alpha > 0;

            // Calculate where that pixel lies within sprite B's bounds
            Vector3 positionB = new Vector3(i,j,0) * a.WorldMatrix * b.InverseWorldMatrix;

            // If it's outside bounds skip to the next pixel
            if(positionB.X<0 || positionB.Y<0 || 
               positionB.X>=b.Width || positionB.Y>=b.Height) continue;

            // Check if pixel is solid in sprite B
            bool solidB = b.getPixelAt(positionB.X, positionB.Y).Alpha > 0;

            // If both are solid then report collision
            if(solidA && solidB) return true;
        }
    }
    return false;
}
David Gouveia
sumber
1
Saya suka ini karena elegan, tetapi menggunakan matriks seperti ini - mengalikan matriks 3x3 per piksel per bingkai - akan memperkenalkan beberapa hukuman kinerja yang cukup serius untuk bitmap terkecil kecuali apa pun, terutama pada sebagian besar perangkat Android.
3Dave
2
@ DavidLive Memang akan, ini merupakan proses yang mahal secara komputasi. Dan saya pikir bahkan dengan kalkulasi matriks yang tidak dimasukkan ke dalam kalkulasi reguler dengan trigonometri - mungkin meninggalkan penskalaan jika itu tidak digunakan - masih akan hampir sama. Mungkin ada solusi lain, tetapi saya tidak bisa memikirkannya. Pada akhirnya solusi terbaik adalah merenungkan apakah deteksi tabrakan pixel sempurna benar-benar diperlukan. Dan beberapa game yang menggunakan tabrakan pixel sempurna (seperti game Sonic pertama) mengabstraksi karakter menjadi serangkaian titik tabrakan.
David Gouveia
poin bagus; Saya mencoba otak saya untuk mencari solusi alternatif yang tidak melibatkan pengurangan bitmap dan mencari puncak. Mungkin ada peluang untuk makalah di tempat ini. :) Mantra: optimal vs cukup ... optimal vs cukup ..
3Dave
Terima kasih banyak, itu penjelasan yang sangat bagus. Saat ini saya memiliki pengaturan tabrakan berbatasan mengatur dan jika itu terjadi maka saya memeriksa pixel tumpang tindih antara dua bitmap tetapi hanya dalam persegi panjang yang tumpang tindih. Ini semua bekerja dengan baik dan tidak terlalu intensif prosesor. Masalahnya terjadi ketika saya memutar gambar. Piksel itu sendiri tidak dirotasi dalam bitmap. Saya hanya menggambar gambar yang diputar. Jadi ketika saya terus memeriksa tabrakan piksel itu masih menggunakan piksel lama. Saya suka apa yang Anda katakan tentang pemetaan piksel ke koordinat dunia. Ini mungkin yang harus saya lakukan. Terima kasih!!!
DRiFTy
+1 Saya selalu bertanya-tanya bagaimana algoritme bekerja, Terima kasih @DavidGouveia
Wisnu
2

Sementara jawaban David Gouveia kedengarannya benar, itu bukan solusi terbaik dari sudut pandang kinerja. Ada beberapa optimasi penting yang perlu Anda lakukan:

  1. memilah dan menghindari pemeriksaan yang tidak perlu dengan memeriksa terlebih dahulu dengan tabrakan lingkaran sederhana: untuk mendapatkan pusat dan jari-jari sprite Anda dalam rotasi apa pun, dapatkan koordinat minimum dan maksimum x dan y dari semua 4 (sudah diputar) simpul: kemudian Anda dapat membuat lingkaran yang menutup sprite dengan:

    center = max_x-min_x / 2, max_y-min_y / 2

    radius = max (max_x-min_x, max_y-min_y)

  2. Sekarang Anda seharusnya tidak memiliki terlalu banyak kandidat untuk diperiksa dengan rasterisasi gambar yang sudah diubah (diputar) pada dasarnya dengan menggunakan algoritma texturemapping sederhana . Pada dasarnya Anda melacak 4 baris per sprite raster: 2 baris dari vertex ke verteks berikutnya dari kotak Anda yang diputar (b dan c) 2 baris masuk dalam "ruang bitmap" dari vertex u1 / v1 ke simpul berikutnya u2 / v2 dan u3 / v3: Catatan: Saya mencari di Google gambar ini dan itu menunjukkan segitiga, kotak Anda hanya dua segitiga. Sangat penting bagi algoritma ini untuk menggambar garis- garis horizontal (itu sebabnya disebut " rasterizer ") untuk menghindari "lubang" di dalam bitmap karena kesalahan pembulatan. Dengan menghitung garis dengan algoritma bresenham, Anda perlu untuk setiap pixel hanya 4 tambahan dan 4 membandingkan (dan kadang-kadang dua tambahan tambahan, tergantung pada kemiringan) Apa yang Anda lakukan adalah Anda menulis texturemapper poligon Anda sendiri, namun tanpa koreksi 3d yang mahal (dan sulit dioptimalkan). affine texturemapping

  3. Anda dapat dengan mudah mengurangi resolusi bitmap tabrakan (misalnya dengan faktor 2) dan menghemat lebih banyak waktu. pemeriksaan tabrakan pada setengah dari resolusi shouild menjadi tidak terlalu mencolok.

  4. Jika Anda menggunakan akselerasi grafik, Anda dapat menggunakan semacam cek Buffer yang dipercepat HW (stensil?) Untuk menghindari pengkodean rasterizer sendiri.

Masalah utama adalah: Java tidak terlalu cepat dalam mengakses data bitmap yang disimpan dalam array 2D. Saya akan merekomendasikan untuk menyimpan data dalam array 1 dimensi untuk menghindari setidaknya 1 indexOutOfBounds memeriksa setiap akses. Juga gunakan dimensi power-of-2 (seperti 64x64, 128x128, dll. Dengan ini Anda dapat menghitung offset melalui bitshifting dan bukan multiplikasi). Anda juga dapat mengoptimalkan akses tekstur kedua untuk melakukannya hanya jika yang pertama memiliki nilai! = 0 (transparan)

Semua masalah ini diselesaikan dalam mesin render perangkat lunak, mungkin berguna untuk melihat kode sumber

Peter Parker
sumber