Bagaimana cara Interpolasi antara dua kondisi game?

24

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.

SerangHobo
sumber
2
ticks ticks logic? Jadi, pembaruan Anda fps <rendering fps?
Bebek Komunis
Saya mengubah istilah. Tapi ya logikanya kutu. Dan tidak, saya ingin sepenuhnya merender rendering dari pembaruan, sehingga game dapat di-render pada 120HZ atau 22.8HZ dan pembaruan akan tetap berjalan dengan kecepatan yang sama, asalkan pengguna memenuhi persyaratan sistem.
AttackingHobo
ini mungkin benar-benar rumit karena saat merender semua posisi objek Anda harus tetap diam (mengubahnya selama proses render dapat menyebabkan beberapa perilaku tidak terdefinisi)
Ali1S232
Interpolasi akan menghitung keadaan pada waktu antara 2 frame pembaruan yang sudah dihitung. Bukankah pertanyaan ini tentang ekstrapolasi, menghitung negara untuk beberapa saat setelah bingkai pembaruan terakhir? Karena pembaruan berikutnya bahkan belum dihitung.
Maik Semder
Saya pikir jika dia hanya memperbarui / merender satu utas, tidak dapat memperbarui kembali hanya posisi rendering. Anda hanya mengirim posisi ke GPU dan kemudian memperbarui kembali.
zacharmarz

Jawaban:

22

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 velocitydan position. Untuk menemukan posisi objek pada frame berikutnya, kita cukup menambahkan velocity * draw_timestepke posisi objek saat ini, untuk menemukan posisi prediksi frame berikutnya. draw_timestepadalah 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 positiondan previous_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 sebagai previous_state[0]dan previous_state[1]. Itu juga membutuhkan dua salinannya current_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]ke previous_state[state_index], dan menyalin data baru yang relevan untuk menggambar, positiondan rotationke current_state[state_index]. Kemudian state_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 elapsedjumlah waktu yang telah berlalu di utas render, sejak kutu pembaruan terakhir, dan update_tick_lengthadalah jumlah waktu yang dibutuhkan laju pembaruan tetap Anda per kutu (mis. Pada pembaruan 20FPS, update_tick_length = 0.05).

Jika Anda tidak tahu apa Lerpfungsi 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.

Olhovsky
sumber
1
+1 hal yang sama harus dilakukan untuk orientasi / rotasi dan semua status lainnya yang berubah seiring waktu, misalnya, seperti animasi materi dalam sistem partikel, dll.
Maik Semder
1
Poin bagus Maik, saya hanya menggunakan posisi sebagai contoh. Anda perlu menyimpan "kecepatan" dari setiap properti yang ingin Anda ekstrapolasi (yaitu laju perubahan dari waktu ke waktu properti itu), jika Anda ingin menggunakan ekstrapolasi. Pada akhirnya, saya benar-benar tidak bisa memikirkan situasi di mana ekstrapolasi lebih baik daripada interpolasi, saya hanya memasukkannya karena pertanyaan penanya memintanya. Saya menggunakan interpolasi. Dengan interpolasi, kami perlu menyimpan hasil pembaruan saat ini dan sebelumnya dari setiap properti untuk diinterpolasi, seperti yang Anda katakan.
Olhovsky
Ini adalah penyajian kembali masalah dan perbedaan antara interpolasi dan ekstrapolasi; itu bukan jawaban.
1
Dalam contoh saya, saya menyimpan posisi dan rotasi di negara. Anda bisa menyimpan kecepatan (atau kecepatan) di negara bagian juga. Kemudian Anda lerp antara kecepatan dengan cara yang sama persis ( 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.
Olhovsky
1
Untuk interpolasi gerakan sudut disarankan untuk menggunakan slerp bukan lerp. Paling mudah adalah menyimpan angka empat dari kedua negara dan menyelinap di antara mereka. Kalau tidak, aturan yang sama berlaku untuk kecepatan sudut dan percepatan sudut. Apakah Anda memiliki test case untuk animasi kerangka?
Maik Semder
-2

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.

Mickey
sumber
1
Dia tidak memiliki tingkat pembaruan variabel. Ia memiliki tingkat pembaruan yang tetap. Sejujurnya, saya benar-benar tidak tahu apa yang ingin Anda
sampaikan di
1
??? -1. Itulah intinya, saya memiliki tingkat pembaruan dijamin, tetapi tingkat render variabel, dan saya ingin itu lancar tanpa gagap.
AttackingHobo
Kecepatan pembaruan variabel tidak berfungsi dengan baik dengan game jaringan, game kompetitif, sistem replay, atau apa pun yang bergantung pada game-play yang bersifat deterministik.
AttackingHobo
1
Memperbaiki pembaruan juga memungkinkan integrasi gesekan semu yang mudah. Misalnya jika Anda ingin melipatgandakan kecepatan Anda dengan 0,9 setiap frame, bagaimana Anda mengetahui berapa banyak untuk dikalikan dengan jika Anda memiliki frame cepat atau lambat? Pembaruan tetap kadang-kadang sangat disukai - hampir semua simulasi fisika menggunakan laju pembaruan tetap.
Olhovsky
2
Jika saya menggunakan frame rate variabel, dan mengatur keadaan awal yang kompleks dengan banyak objek saling memantul, tidak ada jaminan bahwa itu akan mensimulasikan persis sama. Bahkan kemungkinan besar akan mensimulasikan sedikit berbeda setiap kali, dengan perbedaan kecil di awal, peracikan dalam waktu singkat menjadi keadaan yang sangat berbeda antara masing-masing menjalankan simulasi.
AttackingHobo