Ulangan hantu - penyimpanan dan waktu

11

Saya sedang mengerjakan game balapan mobil dan baru saja mengimplementasikan sprite hantu untuk mengulang balapan sebelumnya. Saya menggunakan mesin fisika dan setelah banyak membaca saya sampai pada kesimpulan bahwa cara terbaik untuk menyimpan data hantu untuk replay adalah dengan merekam posisi dan rotasi mobil pada titik waktu tertentu, seperti misalnya dijelaskan di sini: https: // gamedev. stackexchange.com/a/8380/26261 .

Tapi apa cara yang baik untuk menemukan titik waktu selama replay? Contoh akan menjadi catatan dengan data ini:

time: +3.19932 (seconds since race start)
position:  180,40 (position at that time)
rotation: 30.4 (rotation at that time)

Tapi saya punya beberapa masalah dengan itu:

  1. Ketika saya memutar ulang, tidak mungkin saya mencapai titik waktu yang tepat di 3.19932 lagi - lebih mungkin, saya akan memiliki titik waktu sekitar 3.1 dan harus menemukan catatan pencocokan terdekat. Saat interpolasi, bahkan pencocokan terdekat di atas dan di bawah. Ini kedengarannya sangat tidak efisien dan memakan waktu?

  2. Di struktur daftar mana saya bisa menyimpan catatan ini untuk pemutaran ulang nanti? Array? Bukankah itu berarti bahwa waktu pencarian untuk catatan yang cocok dengan waktu tertentu akan meningkat semakin lama balapan?

  3. Frekuensi mana yang harus saya gunakan untuk titik waktu? Setiap frame akan menjadi - saya kira - berlebihan, bukan saya harus menyimpan yaitu setiap frame n dan interpolasi di antaranya, yang membuat pertanyaan penyimpanan di 2. bahkan lebih sulit.

Jadi, apakah gagasan ini bahkan merupakan pendekatan yang tepat? Jika ya, bagaimana saya bisa menyimpan dan mengambil data secara efisien? Harap dicatat bahwa saya biasanya ingin menggunakan struktur data di atas, bukan gamestate deterministik dan merekam input pengguna, dll.

Terima kasih atas bantuannya!

EDIT: Saya sadar saya harus menggambarkan lingkungan yang saya gunakan: Cocos2D untuk iPhone. Ada sebuah metode update:(ccTime)delta. Idealnya, metode ini akan dipanggil setiap 1/60 detik, tetapi tidak ada jaminan - deltaadalah waktu yang sebenarnya berlalu sejak gametick terakhir dan bisa lebih atau kurang dari 1/60. Dalam metode ini saya ingin menyimpan gamestate saat ini.

marimba
sumber
2
Pertanyaan yang sangat bagus Seperti yang diperlihatkan ini, replay yang tepat lebih kompleks daripada yang mungkin Anda pikirkan pada awalnya, dan saya penasaran untuk melihat solusi apa yang muncul di sini.
Christian

Jawaban:

8

Bukankah itu berarti bahwa waktu pencarian untuk catatan yang cocok dengan waktu tertentu akan meningkat semakin lama balapan?

Tidak :)

Katakanlah Anda menyimpannya sebagai array (perhatikan snapshot dalam urutan kronologis, tetapi tidak merata spasi):

snapshots = [
    {time: 0.0, position: {x,y,z}},
    {time: 0.41,    position: {x,y,z}},
    {time: 0.57,    position: {x,y,z}},
    {time: 1.10,    position: {x,y,z}},
    {time: 1.67,    position: {x,y,z}},
    {time: 2.05,    position: {x,y,z}},
    {time: 3.24,    position: {x,y,z}},
    {time: 3.86,    position: {x,y,z}},
    {time: 3.91,    position: {x,y,z}},
    {time: 5.42,    position: {x,y,z}},
    ...]

Lalu, ketika replay / game dimulai, Anda mendapatkan elemen pertama dan kedua dari array:

nextIdx = 1
previousSnapshot = snapshots[nextIdx-1]
nextSnapshot = snapshots[nextIdx]

Kemudian di setiap frame ( currentTimeadalah waktu saat ini di game baru ini):

if currentTime > nextSnapshot.time
    nextIdx++
    previousSnapshot = snapshots[nextIdx-1]
    nextSnapshot = snapshots[nextIdx]

# Do your magic here, e.g.:
snapshotPairGap = nextSnapshot.time - previousSnapshot.time
ratio = (currentTime - previousSnapshot.time) / snapshotPairGap
ghostPosition = {
    x: previousSnapshot.position.x + ratio*(nextSnapshot.position.x - previousSnapshot.position.x)
    y: previousSnapshot.position.y + ratio*(nextSnapshot.position.y - previousSnapshot.position.y)
    z: previousSnapshot.position.z + ratio*(nextSnapshot.position.z - previousSnapshot.position.z)
}

Tentu saja ini bisa dioptimalkan dengan caching beberapa perhitungan. Tidak ada pencarian melalui array, hanya mencari indeks tertentu.

Supr
sumber
IYA! Saya harus mencoba ini nanti, tetapi sepertinya ini yang saya cari. Terima kasih!!
marimba
15

Itu tidak terlalu sulit. Anda dapat menyimpan data Anda pada titik waktu yang sewenang-wenang (semakin banyak, semakin baik), dan Anda dapat menginterpolasi nilai data berdasarkan cap waktu yang Anda cari dan data dari dua cap waktu terdekat, misalnya:

N | Time | Position | Rotation
1 | 0.05 | 1, 1, 1  | 0
2 | 0.15 | 1, 2, 1  | 0
3 | 0.25 | 1, 3, 2  | 30

Sekarang bayangkan Anda ingin mendapatkan posisi dan rotasi pada waktu 0,10. Karena 0,10 berada di antara poin '1' (berarti 0,05 waktu) dan '2' (berarti 0,15 waktu), Anda perlu menginterpolasi ini.

timestamp = 0.10
factor = (timestamp - Time[1]) / (Time[2] - Time[1])
position = Lerp(Position[1], Position[2], factor)
rotation = Lerp(Rotation[1], Rotation[2], factor)

Lerphanya interpolasi linier .

Jadi mari kita isi celah dengan beberapa contoh (*).

N | Time  | Position    | Rotation
1 | 0.05  | 1, 1,    1  | 0
* | 0.075 | 1, 1.25, 1  | 0
* | 0.10  | 1, 1.5,  1  | 0
2 | 0.15  | 1, 2,    1  | 0
* | 0.20  | 1, 2.5,  2  | 15
3 | 0.25  | 1, 3,    2  | 30

HTH.

Marcin Seredynski
sumber
5
+1. Interpolasi adalah jawaban yang sederhana dan efektif di sini. Mungkin benar bahwa interpolasi kubik mungkin memberikan hasil yang sedikit lebih baik ketika kendaraan berputar tetapi linier akan bekerja dengan baik jika intervalnya cukup kecil.
Kylotan
Terima kasih telah menunjukkan cara menginterpolasi! Ini akan sangat berguna untuk gim saya. Tetapi katakanlah saya ingin mengambil pada waktu 41,15, jauh di dalam array. Apakah Anda akan mulai mencari melalui seluruh array sampai Anda menemukan catatan> 41,15?
marimba
1
Pencarian linear sederhana dapat bekerja untuk Anda, tetapi pencarian biner lebih baik, ketika Anda memiliki array yang diurutkan: en.wikipedia.org/wiki/Binary_search_algorithm
Marcin Seredynski