Saya telah mengajukan beberapa pertanyaan serupa selama 8 bulan terakhir atau lebih tanpa sukacita, jadi saya akan membuat pertanyaan lebih umum.
Saya memiliki game Android yaitu OpenGL ES 2.0. di dalamnya saya memiliki Loop Game berikut:
Loop saya berfungsi pada prinsip langkah waktu tetap (dt = 1 / ticksPerSecond )
loops=0;
while(System.currentTimeMillis() > nextGameTick && loops < maxFrameskip){
updateLogic(dt);
nextGameTick+=skipTicks;
timeCorrection += (1000d/ticksPerSecond) % 1;
nextGameTick+=timeCorrection;
timeCorrection %=1;
loops++;
}
render();
Intergrasi saya berfungsi seperti ini:
sprite.posX+=sprite.xVel*dt;
sprite.posXDrawAt=sprite.posX*width;
Sekarang, semuanya berjalan cukup seperti yang saya inginkan. Saya dapat menentukan bahwa saya ingin objek bergerak melintasi jarak tertentu (lebar layar katakan) dalam 2,5 detik dan itu akan melakukan hal itu. Juga karena lompatan frame yang saya izinkan dalam loop game saya, saya bisa melakukan ini pada hampir semua perangkat dan itu akan selalu memakan waktu 2,5 detik.
Masalah
Namun, masalahnya adalah bahwa ketika membuat bingkai melompati, grafik gagap. Sangat menyebalkan. Jika saya menghapus kemampuan untuk melompati frame, maka semuanya lancar seperti yang Anda inginkan, tetapi akan berjalan pada kecepatan yang berbeda pada perangkat yang berbeda. Jadi itu bukan pilihan.
Saya masih tidak yakin mengapa frame dilewati, tetapi saya ingin menunjukkan bahwa ini tidak ada hubungannya dengan kinerja yang buruk , saya telah mengambil kode kembali ke 1 sprite kecil dan tidak ada logika (terlepas dari logika yang diperlukan untuk pindahkan sprite) dan saya masih mendapatkan bingkai yang dilewati. Dan ini ada di tablet Google Nexus 10 (dan seperti yang disebutkan di atas, saya perlu melewatkan bingkai untuk menjaga kecepatan konsisten di semua perangkat).
Jadi, satu-satunya pilihan lain yang saya miliki adalah menggunakan interpolasi (atau ekstrapolasi), saya sudah membaca setiap artikel yang ada di luar sana tetapi tidak ada yang benar-benar membantu saya untuk memahami cara kerjanya dan semua upaya implementasi saya gagal.
Dengan menggunakan satu metode, saya bisa membuat semuanya bergerak dengan lancar tetapi tidak bisa dilakukan karena mengacaukan benturan saya. Saya dapat meramalkan masalah yang sama dengan metode serupa karena interpolasi diteruskan ke (dan ditindaklanjuti dalam) metode rendering - pada waktu render. Jadi jika Collision memperbaiki posisi (karakter sekarang berdiri tepat di sebelah dinding), maka penyaji dapat mengubah posisi itu dan menggambarnya di dinding.
Jadi saya benar-benar bingung. Orang-orang mengatakan bahwa Anda tidak boleh mengubah posisi objek dari dalam metode rendering, tetapi semua contoh online menunjukkan ini.
Jadi saya meminta dorongan ke arah yang benar, tolong jangan menautkan ke artikel loop game populer (deWitters, Perbaiki cap waktu Anda, dll) karena saya sudah membaca ini beberapa kali . Saya tidak meminta siapa pun untuk menulis kode saya untuk saya. Jelaskan secara sederhana bagaimana Interpolasi sebenarnya bekerja dengan beberapa contoh. Saya kemudian akan pergi dan mencoba untuk mengintegrasikan ide-ide apa pun ke dalam kode saya dan akan mengajukan pertanyaan yang lebih spesifik jika perlu lebih jauh ke depan. (Saya yakin ini adalah masalah yang banyak orang perjuangkan).
sunting
Beberapa informasi tambahan - variabel yang digunakan dalam loop game.
private long nextGameTick = System.currentTimeMillis();
//loop counter
private int loops;
//Amount of frames that we will allow app to skip before logic is affected
private final int maxFrameskip = 5;
//Game updates per second
final int ticksPerSecond = 60;
//Amount of time each update should take
private final int skipTicks = (1000 / ticksPerSecond);
float dt = 1f/ticksPerSecond;
private double timeCorrection;
sumber
Jawaban:
Ada dua hal penting untuk membuat gerakan tampak mulus, yang pertama jelas bahwa apa yang Anda render harus mencocokkan dengan kondisi yang diharapkan pada saat bingkai disajikan kepada pengguna, yang kedua adalah Anda perlu menampilkan bingkai kepada pengguna pada interval yang relatif tetap. Menyajikan bingkai pada T + 10ms, lalu yang lain pada T + 30ms, lalu yang lain pada T + 40ms, akan tampak bagi pengguna untuk menilai, bahkan jika apa yang sebenarnya ditunjukkan untuk waktu itu adalah benar sesuai dengan simulasi.
Loop utama Anda tampaknya tidak memiliki mekanisme gating untuk memastikan bahwa Anda hanya membuat secara berkala. Jadi kadang-kadang Anda mungkin melakukan 3 pembaruan antara render, kadang-kadang Anda mungkin 4. Pada dasarnya loop Anda akan merender sesering mungkin, segera setelah Anda mensimulasikan cukup waktu untuk mendorong keadaan simulasi di depan waktu saat ini, Anda akan lalu berikan status itu. Tetapi setiap variabilitas dalam berapa lama waktu yang dibutuhkan untuk memperbarui atau membuat, dan interval antara frame akan bervariasi juga. Anda memiliki stempel waktu tetap untuk simulasi Anda, tetapi stempel waktu variabel untuk rendering Anda.
Apa yang mungkin Anda butuhkan adalah menunggu tepat sebelum render Anda, yang memastikan bahwa Anda hanya akan mulai render pada awal interval render. Idealnya harus adaptif: jika Anda telah terlalu lama memperbarui / membuat dan awal interval telah berlalu, Anda harus merender segera, tetapi juga meningkatkan panjang interval, sampai Anda dapat secara konsisten membuat dan memperbarui dan masih dapat render berikutnya sebelum interval selesai. Jika Anda memiliki banyak waktu luang, maka Anda dapat secara perlahan mengurangi interval (yaitu meningkatkan frame rate) untuk membuat lebih cepat lagi.
Tapi, dan inilah kickernya, jika Anda tidak membuat frame segera setelah mendeteksi bahwa keadaan simulasi telah diperbarui menjadi "sekarang", maka Anda memperkenalkan alias sementara. Bingkai yang disajikan kepada pengguna disajikan pada waktu yang sedikit salah, dan itu sendiri akan terasa seperti gagap.
Ini adalah alasan untuk "timestep parsial" yang akan Anda lihat disebutkan dalam artikel yang telah Anda baca. Itu ada di sana untuk alasan yang bagus, dan itu karena kecuali jika Anda memperbaiki catatan waktu fisika Anda ke beberapa kelipatan terpisahkan tetap dari catatan waktu pembuatan render tetap Anda, Anda tidak bisa menampilkan bingkai pada waktu yang tepat. Anda akhirnya mempresentasikannya terlalu dini, atau terlalu terlambat. Satu-satunya cara untuk mendapatkan tingkat rendering yang tetap dan masih menyajikan sesuatu yang benar secara fisik, adalah dengan menerima bahwa pada saat interval rendering muncul, Anda kemungkinan besar akan berada di tengah-tengah antara dua catatan waktu fisika tetap Anda. Tetapi itu tidak berarti bahwa objek-objek tersebut dimodifikasi selama rendering, Hanya saja rendering harus menetapkan sementara di mana objek berada sehingga dapat membuat mereka di suatu tempat di antara di mana mereka sebelumnya dan di mana mereka setelah pembaruan. Itu penting - jangan pernah mengubah negara dunia untuk rendering, hanya pembaruan yang harus mengubah negara dunia.
Jadi untuk memasukkannya ke dalam loop pseudocode, saya pikir Anda membutuhkan sesuatu yang lebih seperti:
Agar hal ini berhasil, semua objek yang diperbarui perlu mempertahankan pengetahuan di mana mereka sebelumnya dan di mana mereka sekarang, sehingga rendering dapat menggunakan pengetahuan tentang di mana objek itu berada.
Dan mari kita paparkan timeline dalam milidetik, mengatakan rendering membutuhkan 3ms untuk menyelesaikan, memperbarui butuh 1ms, langkah waktu pembaruan Anda ditetapkan menjadi 5ms, dan catatan waktu render Anda mulai (dan tetap) pada 60ms [60Hz].
Ada nuansa lain di sini tentang simulasi terlalu jauh sebelumnya, yang berarti input pengguna mungkin diabaikan meskipun terjadi sebelum bingkai benar-benar dibuat, tetapi jangan khawatir tentang hal itu sampai Anda yakin bahwa loop tersebut mensimulasikan dengan lancar.
sumber
Apa yang semua orang katakan adalah benar. Jangan pernah memperbarui posisi simulasi sprite Anda dalam logika render Anda.
Pikirkan seperti ini, sprite Anda memiliki 2 posisi; di mana simulasi mengatakan dia pada pembaruan simulasi terakhir, dan di mana sprite diberikan. Mereka adalah dua koordinat yang sangat berbeda.
Sprite diberikan pada posisi ekstrapolasi. Posisi ekstrapolasi dihitung setiap frame render, digunakan untuk membuat sprite, lalu dibuang. Hanya itu yang ada untuk itu.
Selain itu, Anda tampaknya memiliki pemahaman yang baik. Semoga ini membantu.
sumber