Ekstrapolasi merusak deteksi tabrakan

10

Sebelum menerapkan ekstrapolasi pada gerakan sprite saya, tabrakan saya bekerja dengan sempurna. Namun, setelah menerapkan ekstrapolasi pada gerakan sprite saya (untuk menghaluskan semuanya), tabrakan tidak lagi berfungsi.

Ini adalah cara kerja sebelum ekstrapolasi:

masukkan deskripsi gambar di sini

Namun, setelah saya menerapkan ekstrapolasi, rutin tabrakan rusak. Saya berasumsi ini karena bertindak berdasarkan koordinat baru yang telah dihasilkan oleh rutin ekstrapolasi (yang terletak di panggilan render saya).

Setelah saya menerapkan ekstrapolasi saya

masukkan deskripsi gambar di sini

Bagaimana cara memperbaiki perilaku ini?

Saya telah mencoba melakukan pemeriksaan tabrakan ekstra setelah ekstrapolasi - ini tampaknya untuk menjernihkan banyak masalah, tetapi saya telah mengesampingkan hal ini karena menempatkan logika ke dalam rendering saya adalah keluar dari pertanyaan.

Saya juga telah mencoba membuat salinan dari posisi sprite X, memperkirakan itu dan menggambar menggunakan itu daripada yang asli, sehingga meninggalkan keaslian logika untuk melanjutkan - ini tampaknya pilihan yang lebih baik, tetapi masih menghasilkan beberapa efek aneh saat bertabrakan dengan dinding. Saya cukup yakin ini juga bukan cara yang tepat untuk menangani ini.

Saya telah menemukan beberapa pertanyaan serupa di sini tetapi jawabannya belum membantu saya.

Ini adalah kode ekstrapolasi saya:

public void onDrawFrame(GL10 gl) {


        //Set/Re-set loop back to 0 to start counting again
        loops=0;

        while(System.currentTimeMillis() > nextGameTick && loops < maxFrameskip){

        SceneManager.getInstance().getCurrentScene().updateLogic();
        nextGameTick+=skipTicks;
        timeCorrection += (1000d/ticksPerSecond) % 1;
        nextGameTick+=timeCorrection;
        timeCorrection %=1;
        loops++;
        tics++;

     }

        extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks; 

        render(extrapolation);
}

Menerapkan ekstrapolasi

            render(float extrapolation){

            //This example shows extrapolation for X axis only.  Y position (spriteScreenY is assumed to be valid)
            extrapolatedPosX = spriteGridX+(SpriteXVelocity*dt)*extrapolation;
            spriteScreenPosX = extrapolationPosX * screenWidth;

            drawSprite(spriteScreenX, spriteScreenY);           


        }

Edit

Seperti yang saya sebutkan di atas, saya telah mencoba membuat salinan koordinat sprite khusus untuk menggambar .... ini memiliki masalah sendiri.

Pertama, terlepas dari penyalinannya, ketika sprite bergerak, itu sangat halus, ketika berhenti, bergetar sedikit ke kiri / kanan - karena masih memperkirakan posisi itu berdasarkan waktu. Apakah ini perilaku normal dan dapatkah kita 'mematikannya' ketika sprite berhenti?

Saya sudah mencoba bendera untuk kiri / kanan dan hanya memperkirakan jika salah satu dari ini diaktifkan. Saya juga telah mencoba menyalin posisi terakhir dan saat ini untuk melihat apakah ada perbedaan. Namun, sejauh tabrakan berlangsung, ini tidak membantu.

Jika pengguna menekan katakan, tombol kanan dan sprite bergerak ke kanan, ketika menyentuh dinding, jika pengguna terus menekan tombol kanan ke bawah, sprite akan terus menjiwai ke kanan, sementara dihentikan oleh dinding ( Oleh karena itu tidak benar-benar bergerak), namun karena bendera kanan masih diatur dan juga karena rutin tabrakan terus-menerus memindahkan sprite keluar dari dinding, itu masih tampak pada kode (bukan pemain) bahwa sprite masih bergerak, dan oleh karena itu ekstrapolasi terus berlanjut. Jadi apa yang akan dilihat pemain, adalah sprite 'statis' (ya, itu menjiwai, tapi itu tidak benar-benar bergerak melintasi layar), dan kadang-kadang bergetar hebat ketika ekstrapolasi berusaha melakukan itu ..... .. Semoga bantuan ini

BungleBonce
sumber
1
Ini akan membutuhkan beberapa intisari untuk memahami sepenuhnya ('interpolasi' tampaknya memiliki selusin makna yang berbeda dan itu tidak sepenuhnya jelas pada pandangan pertama hanya apa yang Anda maksud dengan itu di sini), tetapi insting pertama saya adalah 'Anda tidak boleh melakukan apa pun untuk mempengaruhi Anda posisi objek dalam rutin rendering Anda '. Penyaji Anda harus menggambar objek Anda pada posisi yang diberikan objek, dan memanipulasi objek ada resep untuk masalah, karena itu secara inheren memasangkan penyaji ke fisika permainan. Di dunia yang ideal, penyaji Anda harus bisa membawa pointer ke objek permainan.
Steven Stadnicki
Hai @StevenStadnicki, terima kasih banyak atas komentar Anda, ada banyak contoh yang menunjukkan nilai interpolasi yang diteruskan ke penyaji, silakan lihat ini: mysecretroom.com/www/programming-and-software/… yang berasal dari tempat saya mengadaptasi kode. Pemahaman saya yang terbatas adalah bahwa ini menginterpolasi posisi antara pembaruan terakhir dan berikutnya berdasarkan jumlah waktu yang diambil sejak pembaruan terakhir - Saya setuju ini sedikit mimpi buruk! Saya akan sangat berterima kasih jika Anda bisa mengusulkan dan alternatif saya lebih mudah untuk bekerja dengan - bersorak :-)
BungleBonce
6
Baiklah, saya akan mengatakan 'hanya karena Anda dapat menemukan kode untuk sesuatu tidak menjadikannya praktik terbaik'. :-) Dalam kasus ini, saya menduga bahwa halaman yang Anda tautkan menggunakan nilai interpolasi untuk mencari tahu di mana menampilkan objeknya, tetapi itu tidak benar-benar memperbarui posisi objek dengan mereka; Anda dapat melakukannya juga, hanya dengan memiliki posisi khusus menggambar yang dihitung setiap frame, tetapi menjaga posisi itu terpisah dari posisi 'fisik' aktual objek.
Steven Stadnicki
Hai @StevenStadnicki, seperti yang dijabarkan dalam pertanyaan saya (paragraf awal "Saya juga sudah mencoba membuat salinan"), saya sebenarnya sudah mencoba menggunakan posisi 'draw only' :-) Bisakah Anda mengusulkan metode interpolasi di mana saya tidak perlu melakukan penyesuaian posisi sprite dalam rutin render? Terima kasih!
BungleBonce
Melihat kode Anda, sepertinya Anda melakukan ekstrapolasi alih-alih interpolasi.
Durza007

Jawaban:

1

Saya belum dapat memposting komentar, jadi saya akan memposting ini sebagai jawaban.

Jika saya memahami masalahnya dengan benar, bisa jadi begini:

  1. pertama Anda memiliki tabrakan
  2. maka posisi objek dikoreksi (mungkin oleh rutin deteksi tabrakan)
  3. posisi objek yang diperbarui dikirim ke fungsi render
  4. fungsi render kemudian memperbarui lokasi objek menggunakan ekstrapolasi
  5. posisi objek yang diekstrapolasi sekarang istirahat rutin deteksi tabrakan

Saya dapat memikirkan 3 solusi yang mungkin. Saya akan mencantumkannya dalam urutan yang paling diinginkan untuk IMHO.

  1. Pindahkan ekstrapolasi dari fungsi render. Ekstrapolasi posisi objek dan kemudian uji tabrakan.
  2. atau jika Anda ingin menyimpan ekstrapolasi dalam fungsi render, atur bendera untuk menunjukkan tabrakan telah terjadi. Minta pendeteksian tabrakan perbaiki posisi objek seperti yang sudah Anda lakukan, tetapi sebelum Anda menghitung nilai ekstrapolasi, periksa bendera tabrakan terlebih dahulu. Karena objek sudah berada di tempat yang diperlukan, tidak perlu untuk memindahkannya.
  3. Kemungkinan terakhir, yang bagi saya lebih merupakan solusi daripada perbaikan adalah dengan kompensasi yang berlebihan dengan deteksi tabrakan. Setelah tabrakan, pindahkan benda menjauh dari dinding, sehingga setelah ekstrapolasi objek kembali ke dinding.

Contoh kode untuk # 2.

if (collisionHasOccured)
    extrpolation = 0.0f;
else
    extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks;

Saya pikir # 2 mungkin akan menjadi yang tercepat dan termudah untuk menjalankan, meskipun # 1 tampaknya menjadi solusi yang lebih akurat secara logis. Bergantung pada bagaimana Anda menangani solusi waktu delta # 1 Anda dapat dipecahkan dengan cara yang sama oleh delta besar, dalam hal ini Anda mungkin harus menggunakan keduanya # 1 dan # 2 bersama-sama.

EDIT: Saya salah mengerti kode Anda sebelumnya. Loop dimaksudkan untuk membuat secepat mungkin dan memperbarui pada interval yang ditentukan. Inilah sebabnya mengapa Anda akan menginterpolasi posisi sprite, untuk menangani kasus di mana Anda menggambar lebih dari memperbarui. Namun, jika loop berada di belakang, maka Anda melakukan polling pada pembaruan sampai Anda tertangkap atau Anda melewatkan jumlah maksimum frame draw.

Yang sedang berkata, satu-satunya masalah adalah objek bergerak setelah tabrakan. Jika ada tabrakan, objek harus berhenti bergerak ke arah itu. Jadi, jika ada tabrakan, maka atur kecepatannya ke 0. Ini seharusnya menghentikan fungsi render dari memindahkan objek lebih jauh.

Aholio
sumber
Hai @Aholio Saya sudah mencoba opsi 2 dan itu berhasil tetapi menyebabkan beberapa gangguan mungkin saya salah melakukannya, saya akan mengunjungi kembali itu. Namun saya sangat tertarik dengan opsi 1 walaupun saya tidak dapat menemukan info tentang cara melakukan ini. Bagaimana saya bisa mengekstrapolasi katakan dalam rutinitas logika saya? BTW delta saya sudah diperbaiki jadi 1/60 atau 0,01667 (lebih tepatnya ini adalah angka yang saya gunakan untuk diintegrasikan meskipun jumlah waktu setiap iterasi dari permainan saya jelas dapat bervariasi tergantung pada apa yang terjadi tetapi tidak boleh benar-benar mengambil lebih dari 0,01667 ) jadi bantuan apa pun akan sangat berterima kasih.
BungleBonce
Mungkin saya tidak sepenuhnya mengerti apa yang Anda lakukan. Sesuatu yang agak aneh bagi saya adalah Anda tidak hanya melakukan gerakan posisi di fungsi menggambar Anda; Anda juga melakukan perhitungan waktu. Apakah ini dilakukan untuk setiap objek? Urutan yang benar adalah: perhitungan waktu yang dilakukan dalam kode loop game. Gim permainan melewati delta ke fungsi pembaruan (dt). Perbarui (dt) akan memperbarui semua objek game. Ini harus menangani setiap gerakan, ekstrapolasi, dan deteksi tabrakan. Setelah pembaruan () kembali ke loop game, ia kemudian memanggil fungsi render () yang menarik semua objek game yang baru diperbarui.
Aholio
Hmmmmm saat ini fungsi pembaruan saya melakukan segalanya. Apakah gerakan (maksud saya menghitung posisi baru sprite - ini dihitung dari waktu delta saya). Satu-satunya hal yang saya lakukan dalam fungsi render saya (selain dari benar-benar menggambar) adalah memperkirakan posisi 'baru', tetapi tentu saja ini menggunakan waktu karena harus memperhitungkan waktu dari pembaruan logika terakhir ke panggilan render. Itulah pengertian saya :-) Bagaimana Anda akan melakukan ini? Saya tidak mengerti cara menggunakan ekstrapolasi hanya dalam pembaruan logika. Terima kasih!
BungleBonce
Memperbarui jawaban saya. Semoga itu bisa membantu
Aholio
Terima kasih, lihat di Q asli saya "ketika berhenti, itu bergoyang sedikit ke kiri / kanan" - jadi bahkan jika saya menghentikan sprite saya, ekstrapolasi masih membuat sprite 'bergerak' (bergoyang) - ini terjadi karena saya tidak menggunakan Posisi lama dan saat ini sprite untuk mengerjakan ekstrapolasi saya, oleh karena itu walaupun sprite itu statis, masih memiliki efek goyangan ini berdasarkan pada nilai 'ekstrapolasi' yang bekerja pada setiap frame iterasi. Saya bahkan sudah mencoba mengatakan 'jika pos lama dan saat ini sama maka jangan memperkirakan' tetapi ini sepertinya tidak berhasil, saya akan kembali dan melihat lagi!
BungleBonce
0

Sepertinya Anda perlu memisahkan rendering dan memperbarui fisika sepenuhnya. Biasanya simulasi yang mendasarinya akan berjalan pada waktu-langkah diskrit, dan frekuensi tidak akan pernah berubah. Misalnya, Anda bisa mensimulasikan pergerakan bola Anda setiap 1/60 detik, dan hanya itu.

Untuk memungkinkan framerate variabel, kode rendering harus beroperasi pada frekuensi variabel, tetapi setiap simulasi harus tetap pada langkah waktu yang tetap. Ini memungkinkan grafik membaca memori dari simulasi sebagai read-only, dan memungkinkan Anda mengatur interpolasi alih-alih ekstrapolasi.

Karena ekstrapolasi mencoba memprediksi di mana nilai-nilai masa depan akan terjadi, perubahan gerakan yang tiba-tiba dapat memberikan Anda kesalahan ekstrapolasi yang sangat besar. Lebih baik memberikan adegan Anda tentang kerangka di belakang simulasi dan menyisipkan di antara posisi yang diketahui terpisah.

Jika Anda ingin melihat beberapa detail implementasi, saya sudah menulis bagian singkat tentang topik ini di sebuah artikel di sini . Silakan lihat bagian yang disebut "Timestepping".

Inilah kode psuedo penting dari artikel:

const float fps = 100
const float dt = 1 / fps
float accumulator = 0

// In units seconds
float frameStart = GetCurrentTime( )

// main loop
while(true)
  const float currentTime = GetCurrentTime( )

  // Store the time elapsed since the last frame began
  accumulator += currentTime - frameStart( )

  // Record the starting of this frame
  frameStart = currentTime

  // Avoid spiral of death and clamp dt, thus clamping
  // how many times the UpdatePhysics can be called in
  // a single game loop.
  if(accumulator > 0.2f)
    accumulator = 0.2f

  while(accumulator > dt)
    UpdatePhysics( dt )
    accumulator -= dt

  const float alpha = accumulator / dt;

  RenderGame( alpha )

void RenderGame( float alpha )
  for shape in game do
    // calculate an interpolated transform for rendering
    Transform i = shape.previous * alpha + shape.current * (1.0f - alpha)
    shape.previous = shape.current
    shape.Render( i )

The RenderGamefungsi yang paling menarik. Idenya adalah untuk menggunakan interpolasi antara posisi simulasi diskrit. Kode rendering dapat membuat salinan sendiri dari data read-only simulasi, dan menggunakan nilai interpolasi sementara untuk merender. Ini akan memberi Anda gerakan yang sangat lancar tanpa masalah konyol seperti apa yang tampaknya Anda alami!

RandyGaul
sumber