Apa pola terbaik untuk membuat sistem yang semua posisi objek diinterpolasi antara dua status pembaruan?
Pembaruan akan selalu berjalan pada frekuensi yang sama, tetapi saya ingin dapat membuat di FPS apa pun. Jadi rendering akan semulus mungkin tanpa peduli frame per detik, apakah lebih rendah, atau lebih tinggi dari frekuensi pembaruan.
Saya ingin memperbarui 1 frame ke interpolasi masa depan dari frame saat ini ke frame masa depan. Jawaban ini memiliki tautan yang berbicara tentang melakukan ini:
Tanda waktu semi-tetap atau Sepenuhnya-diperbaiki?
Sunting: Bagaimana saya bisa menggunakan kecepatan terakhir dan saat ini dalam interpolasi? Misalnya dengan interpolasi linier saja, ia akan bergerak dengan kecepatan yang sama antara posisi. Saya membutuhkan cara untuk menginterpolasi posisi antara dua titik, tetapi pertimbangkan kecepatan pada setiap titik untuk interpolasi. Akan sangat membantu untuk simulasi tingkat rendah seperti efek partikel.
sumber
Jawaban:
Anda ingin memisahkan tingkat pembaruan (kutu logika) dan menggambar (render centang).
Pembaruan Anda akan menghasilkan posisi semua objek di dunia yang akan ditarik.
Saya akan membahas dua kemungkinan berbeda di sini, yang Anda minta, ekstrapolasi, dan juga metode lain, interpolasi.
1.
Ekstrapolasi adalah di mana kita akan menghitung posisi (prediksi) objek pada frame berikutnya, dan kemudian menyisipkan antara posisi objek saat ini, dan posisi objek pada frame berikutnya.
Untuk melakukan ini, setiap objek yang akan ditarik harus memiliki kaitan
velocity
danposition
. Untuk menemukan posisi objek pada frame berikutnya, kita cukup menambahkanvelocity * draw_timestep
ke posisi objek saat ini, untuk menemukan posisi prediksi frame berikutnya.draw_timestep
adalah jumlah waktu yang telah berlalu sejak centang render sebelumnya (alias panggilan undian sebelumnya).Jika Anda membiarkannya di sini, Anda akan menemukan benda itu "berkedip" ketika posisi yang diprediksi tidak cocok dengan posisi aktual di bingkai berikutnya. Untuk menghapus kedipan, Anda dapat menyimpan posisi yang diprediksi, dan lerp antara posisi yang diprediksi sebelumnya dan posisi yang diprediksi baru pada setiap langkah undian, menggunakan waktu yang telah berlalu sejak centang pembaruan sebelumnya sebagai faktor lerp. Ini masih akan menghasilkan perilaku yang buruk ketika objek yang bergerak cepat tiba-tiba mengubah lokasi, dan Anda mungkin ingin menangani kasing khusus itu. Semua yang dikatakan dalam paragraf ini adalah alasan mengapa Anda tidak ingin menggunakan ekstrapolasi.
2.
Interpolasi adalah tempat kami menyimpan status dari dua pembaruan terakhir, dan interpolasi di antara mereka berdasarkan jumlah waktu saat ini yang telah berlalu sejak pembaruan sebelum terakhir. Dalam pengaturan ini, setiap objek harus memiliki yang terkait
position
danprevious_position
. Dalam hal ini, gambar kami akan mewakili paling buruk satu tanda centang pembaruan di belakang gamestate saat ini, dan paling banter, pada kondisi yang sama persis dengan tanda centang pembaruan saat ini.Menurut pendapat saya, Anda mungkin ingin interpolasi seperti yang saya jelaskan, karena lebih mudah dari keduanya untuk diterapkan, dan menggambar sebagian kecil dari satu detik (misalnya 1/60 detik) di belakang negara Anda saat ini diperbarui baik-baik saja.
Edit:
Jika hal di atas tidak cukup untuk memungkinkan Anda melakukan implementasi, berikut adalah contoh cara melakukan metode interpolasi yang telah saya jelaskan. Saya tidak akan membahas ekstrapolasi, karena saya tidak bisa memikirkan skenario dunia nyata di mana Anda harus lebih menyukainya.
Saat Anda membuat objek yang dapat digambar, itu akan menyimpan properti yang perlu digambar (yaitu, informasi keadaan yang diperlukan untuk menggambarnya).
Untuk contoh ini, kami akan menyimpan posisi dan rotasi. Anda mungkin juga ingin menyimpan properti lain seperti posisi koordinat warna atau tekstur (yaitu jika suatu tekstur bergulir).
Untuk mencegah data dari modifikasi saat render thread menggambar itu, (yaitu lokasi satu objek diubah saat render thread menggambar, tetapi semua yang lain belum diperbarui), kita perlu mengimplementasikan beberapa jenis buffering ganda.
Sebuah objek menyimpan dua salinannya
previous_state
. Saya akan menempatkan mereka dalam array dan merujuk mereka sebagaiprevious_state[0]
danprevious_state[1]
. Itu juga membutuhkan dua salinannyacurrent_state
.Untuk melacak salinan buffer ganda yang digunakan, kami menyimpan variabel
state_index
, yang tersedia untuk pembaruan dan undian utas.Utas pembaruan pertama menghitung semua properti objek menggunakan data itu sendiri (struktur data apa pun yang Anda inginkan). Kemudian, itu menyalin
current_state[state_index]
keprevious_state[state_index]
, dan menyalin data baru yang relevan untuk menggambar,position
danrotation
kecurrent_state[state_index]
. Kemudianstate_index = 1 - state_index
, untuk membalik salinan buffer ganda yang saat ini digunakan.Segala sesuatu dalam paragraf di atas harus dilakukan dengan kunci yang diambil
current_state
. Pembaruan dan undian utas mengambil kunci ini. Kunci hanya diambil selama penyalinan informasi negara, yang cepat.Di untaian render, Anda kemudian melakukan interpolasi linier pada posisi dan rotasi seperti ini:
current_position = Lerp(previous_state[state_index].position, current_state[state_index].position, elapsed/update_tick_length)
Di mana
elapsed
jumlah waktu yang telah berlalu di utas render, sejak kutu pembaruan terakhir, danupdate_tick_length
adalah jumlah waktu yang dibutuhkan laju pembaruan tetap Anda per kutu (mis. Pada pembaruan 20FPS,update_tick_length = 0.05
).Jika Anda tidak tahu apa
Lerp
fungsi di atas, maka checkout artikel wikipedia tentang subjek: Linear Interpolasi . Namun, jika Anda tidak tahu apa itu lerping, maka Anda mungkin belum siap untuk mengimplementasikan pembaruan / gambar yang dipisahkan dengan gambar yang diinterpolasi.sumber
Lerp(previous_speed, current_speed, elapsed/update_tick_length)
). Anda dapat melakukan ini dengan nomor yang ingin Anda simpan di negara bagian. Lerping hanya memberi Anda nilai di antara dua nilai, mengingat faktor lerp.Masalah ini mengharuskan Anda untuk memikirkan definisi Anda tentang mulai dan selesai sedikit berbeda. Pemrogram pemula sering berpikir tentang perubahan posisi per frame dan itu adalah cara yang baik untuk pergi di awal. Demi tanggapan saya, mari kita pertimbangkan jawaban satu dimensi.
Katakanlah Anda memiliki monyet di posisi x. Sekarang Anda juga memiliki "addX" yang Anda tambahkan ke posisi monyet per frame berdasarkan keyboard atau kontrol lainnya. Ini akan bekerja selama Anda memiliki frame rate yang dijamin. Katakanlah x Anda adalah 100 dan addX Anda adalah 10. Setelah 10 frame, x + = addX Anda akan terakumulasi menjadi 200.
Sekarang, alih-alih addX, ketika Anda memiliki frame rate variabel, Anda harus berpikir dalam hal kecepatan dan percepatan. Saya akan memandu Anda melalui semua aritmatika ini tetapi sangat sederhana. Yang ingin kami ketahui adalah seberapa jauh Anda ingin melakukan perjalanan per milidetik (1/1000 detik)
Jika Anda memotret untuk 30 FPS, maka velX Anda harus 1/3 dari satu detik (10 frame dari contoh terakhir di 30 FPS) dan Anda tahu bahwa Anda ingin melakukan perjalanan 100 'x' pada waktu itu jadi setel velX Anda ke 100 distance / 10 FPS atau 10 distance per frame. Dalam milidetik, itu berfungsi hingga 1 jarak x per 3,3 milidetik atau 0,3 'x' per milidetik.
Sekarang, setiap kali Anda memperbarui, yang perlu Anda lakukan adalah mencari tahu waktu yang telah berlalu. Apakah 33 ms telah berlalu (1/30 detik) atau apa pun, Anda hanya mengalikan jarak 0,3 dengan jumlah milidetik yang dilewati. Ini berarti bahwa Anda memerlukan timer yang memberi Anda ms (milidetik) akurasi tetapi kebanyakan timer memberi Anda ini. Cukup lakukan sesuatu seperti ini:
var beginTime = getTimeInMillisecond ()
... nanti ...
var time = getTimeInMillisecond ()
var elapsedTime = waktu-beginTime
beginTime = waktu
... sekarang gunakan elapsedTime ini untuk menghitung semua jarak Anda.
sumber