Jadi bagaimana saya mendesain sistem replay?
Anda mungkin mengetahuinya dari game tertentu seperti Warcraft 3 atau Starcraft di mana Anda dapat menontonnya lagi setelah dimainkan.
Anda berakhir dengan file replay yang relatif kecil. Jadi pertanyaan saya adalah:
- Bagaimana cara menyimpan data? (format khusus?) (ukuran file kecil)
- Apa yang harus diselamatkan?
- Bagaimana membuatnya generik sehingga bisa digunakan di gim lain untuk merekam periode waktu (dan bukan pertandingan yang lengkap misalnya)?
- Memungkinkan untuk maju dan mundur (WC3 tidak bisa mundur sejauh yang saya ingat)
Jawaban:
Artikel yang luar biasa ini membahas banyak masalah: http://www.gamasutra.com/view/feature/2029/developing_your_own_replay_system.php
Beberapa hal yang disebutkan dan dilakukan dengan baik oleh artikel ini:
Salah satu cara untuk lebih meningkatkan rasio kompresi untuk sebagian besar kasus adalah dengan memisahkan semua aliran input Anda dan sepenuhnya menyandiaksakan panjangnya secara terpisah. Ini akan menjadi kemenangan atas teknik pengkodean delta jika Anda menyandikan proses Anda dalam 8-bit dan proses itu sendiri melebihi 8 frame (sangat mungkin kecuali permainan Anda adalah tombol hidung belang yang nyata). Saya telah menggunakan teknik ini dalam permainan balapan untuk mengompres input 8 menit dari 2 pemain saat balapan di trek turun menjadi hanya beberapa ratus byte.
Dalam hal membuat sistem seperti itu dapat digunakan kembali, saya telah membuat sistem replay berurusan dengan stream input generik, tetapi juga menyediakan kait untuk memungkinkan logika spesifik game untuk menggerakkan input keyboard / gamepad / mouse ke stream ini.
Jika Anda ingin memutar cepat atau mencari secara acak, Anda dapat menyimpan pos pemeriksaan (gamestate penuh Anda) setiap N bingkai. N harus dipilih untuk meminimalkan ukuran file replay dan juga memastikan waktu yang pemain tunggu untuk masuk akal ketika negara diputar ulang ke titik yang dipilih. Salah satu cara untuk mengatasi ini adalah untuk memastikan bahwa pencarian acak hanya dapat dilakukan ke lokasi pos pemeriksaan yang tepat ini. Memundurkan ulang adalah masalah mengatur kondisi permainan ke pos pemeriksaan tepat sebelum bingkai yang dimaksud, lalu memutar ulang input hingga Anda mencapai frame saat ini. Namun, jika N terlalu besar, Anda bisa memasang setiap beberapa frame. Salah satu cara untuk menghaluskan halangan ini adalah dengan melakukan pra-cache frame yang tidak sinkron antara 2 pos pemeriksaan sebelumnya saat Anda memutar ulang bingkai yang di-cache dari wilayah pos pemeriksaan saat ini.
sumber
Selain solusi "pastikan penekanan tombol dapat diputar ulang", yang bisa sangat sulit, Anda bisa merekam seluruh kondisi permainan di setiap frame. Dengan sedikit kompresi cerdas Anda dapat menekannya secara signifikan. Beginilah cara Braid menangani kode rewinding-waktunya dan berfungsi dengan baik.
Karena bagaimanapun Anda perlu memeriksa titik pemeriksaan untuk memutar ulang, Anda mungkin ingin mencoba mengimplementasikannya dengan cara sederhana sebelum menyulitkan.
sumber
n
detik-detik terakhir dari permainan.Anda dapat melihat sistem Anda seolah-olah terdiri dari serangkaian status dan fungsi, di mana fungsi
f[j]
dengan inputx[j]
mengubah status sistems[j]
menjadi statuss[j+1]
, seperti:Keadaan adalah penjelasan seluruh dunia Anda. Lokasi pemain, lokasi musuh, skor, amunisi yang tersisa, dll. Semua yang Anda butuhkan untuk menggambar bingkai permainan Anda.
Fungsi adalah segala sesuatu yang dapat mempengaruhi dunia. Perubahan bingkai, penekanan tombol, paket jaringan.
Input adalah data fungsi yang diambil. Perubahan bingkai mungkin memerlukan waktu sejak frame terakhir berlalu, penekanan tombol mungkin menyertakan tombol yang sebenarnya ditekan, serta apakah tombol shift ditekan atau tidak.
Demi penjelasan ini, saya akan membuat asumsi berikut:
Asumsi 1:
Jumlah status untuk menjalankan game tertentu jauh lebih besar dari jumlah fungsi. Anda mungkin memiliki ratusan ribu status, tetapi hanya beberapa fungsi (perubahan frame, penekanan tombol, paket jaringan, dll). Tentu saja, jumlah input harus sama dengan jumlah status minus satu.
Asumsi 2:
Biaya spasial (memori, disk) menyimpan satu negara jauh lebih besar daripada menyimpan fungsi dan inputnya.
Asumsi 3:
Biaya temporal (waktu) untuk mempresentasikan suatu negara adalah serupa, atau hanya satu atau dua kali lipat yang lebih panjang daripada menghitung suatu fungsi daripada suatu negara.
Bergantung pada persyaratan sistem replay Anda, ada beberapa cara untuk mengimplementasikan sistem replay, sehingga kami dapat mulai dengan yang paling sederhana. Saya juga akan membuat contoh kecil menggunakan permainan catur, yang direkam pada selembar kertas.
Metode 1:
Toko
s[0]...s[n]
. Ini sangat sederhana, sangat mudah. Karena asumsi 2, biaya spasial ini cukup tinggi.Untuk catur, ini akan dicapai dengan menggambar seluruh papan untuk setiap gerakan.
Metode 2:
Jika Anda hanya perlu meneruskan replay, Anda cukup menyimpan
s[0]
, lalu menyimpanf[0]...f[n-1]
(ingat, ini hanya nama id dari fungsi) danx[0]...x[n-1]
(apa input untuk masing-masing fungsi ini). Untuk memutar ulang, Anda cukup memulais[0]
, dan menghitungdan seterusnya...
Saya ingin membuat anotasi kecil di sini. Beberapa komentator lain mengatakan bahwa permainan "harus deterministik". Siapa pun yang mengatakan bahwa perlu mengambil Ilmu Komputer 101 lagi, karena kecuali permainan Anda dimaksudkan untuk dijalankan pada komputer kuantum, SEMUA PROGRAM KOMPUTER ADALAH DETERMINISTIK¹. Itulah yang membuat komputer sangat mengagumkan.
Namun, karena program Anda kemungkinan besar tergantung pada program eksternal, mulai dari pustaka hingga implementasi CPU yang sebenarnya, memastikan bahwa fungsi Anda berperilaku sama di antara platform mungkin cukup sulit.
Jika Anda menggunakan angka pseudo-acak, Anda bisa menyimpan angka yang dihasilkan sebagai bagian dari input Anda
x
, atau menyimpan status fungsi prng sebagai bagian dari negara Andas
, dan implementasinya sebagai bagian dari fungsif
.Untuk catur, ini akan dicapai dengan menggambar papan awal (yang diketahui) dan kemudian menggambarkan setiap gerakan mengatakan bagian mana yang pergi ke mana. Begitulah cara mereka melakukannya.
Metode 3:
Sekarang, Anda kemungkinan besar ingin mencari replay Anda. Artinya, hitung
s[n]
untuk sewenang-wenangn
. Dengan menggunakan metode 2, Anda perlu menghitungs[0]...s[n-1]
sebelum dapat menghitungs[n]
, yang, menurut asumsi 2, mungkin cukup lambat.Untuk mengimplementasikan ini, metode 3 adalah generalisasi dari metode 1 dan 2: store
f[0]...f[n-1]
danx[0]...x[n-1]
sama seperti metode 2, tetapi juga stores[j]
, untuk semuaj % Q == 0
untuk konstanta yang diberikanQ
. Dalam istilah yang lebih mudah, ini berarti Anda menyimpan bookmark di satu dari setiapQ
negara. Misalnya, untukQ == 100
, Anda menyimpans[0], s[100], s[200]...
Untuk menghitung
s[n]
sewenang-wenangn
, Anda pertama-tama memuat yang disimpan sebelumnyas[floor(n/Q)]
, dan kemudian menghitung semua fungsi darifloor(n/Q)
hinggan
. Paling banyak, Anda akan menghitungQ
fungsi. Nilai yang lebih kecilQ
lebih cepat untuk dihitung tetapi mengkonsumsi lebih banyak ruang, sementara nilai yang lebih besarQ
mengkonsumsi lebih sedikit ruang, tetapi membutuhkan waktu lebih lama untuk menghitung.Metode 3 dengan
Q==1
sama dengan metode 1, sedangkan metode 3 denganQ==inf
sama dengan metode 2.Untuk catur, ini akan dicapai dengan menggambar setiap gerakan, serta satu dari setiap 10 papan (untuk
Q==10
).Metode 4:
Jika Anda ingin membalikkan replay, Anda dapat membuat variasi kecil metode 3. Misalkan
Q==100
, dan Anda ingin menghitungs[150]
melaluis[90]
secara terbalik. Dengan metode 3 yang tidak dimodifikasi, Anda perlu membuat 50 perhitungan untuk mendapatkans[150]
dan kemudian 49 perhitungan lagi untuk mendapatkans[149]
dan seterusnya. Tetapi karena Anda sudah menghitungs[149]
untuk mendapatkannyas[150]
, Anda bisa membuat cache dengans[100]...s[150]
ketika Anda menghitungs[150]
untuk pertama kalinya, dan kemudian Anda sudah adas[149]
dalam cache saat Anda perlu menampilkannya.Anda hanya perlu membuat ulang cache setiap kali Anda perlu menghitung
s[j]
,j==(k*Q)-1
untuk apa pun yang diberikank
. Kali ini, peningkatanQ
akan menghasilkan ukuran yang lebih kecil (hanya untuk cache), tetapi kali lebih lama (hanya untuk membuat ulang cache). Nilai optimal untukQ
dapat dihitung jika Anda mengetahui ukuran dan waktu yang diperlukan untuk menghitung status dan fungsi.Untuk catur, ini akan dicapai dengan menggambar setiap gerakan, serta satu dari setiap 10 papan (untuk
Q==10
), tetapi juga, itu perlu menggambar dalam selembar kertas terpisah, 10 papan terakhir yang telah Anda hitung.Metode 5:
Jika status hanya menghabiskan terlalu banyak ruang, atau fungsi menghabiskan terlalu banyak waktu, Anda dapat membuat solusi yang benar-benar menerapkan (bukan palsu) membalikkan memutar ulang. Untuk melakukan ini, Anda harus membuat fungsi terbalik untuk masing-masing fungsi yang Anda miliki. Namun, ini mensyaratkan bahwa masing-masing fungsi Anda adalah injeksi. Jika ini bisa dilakukan, maka untuk
f'
menunjukkan fungsi terbalikf
, menghitungs[j-1]
sesederhanaPerhatikan bahwa di sini, fungsi dan input keduanya
j-1
, tidakj
. Fungsi dan input yang sama ini akan menjadi yang akan Anda gunakan jika Anda menghitungMembuat kebalikan dari fungsi-fungsi ini adalah bagian yang sulit. Namun, Anda biasanya tidak bisa, karena beberapa data keadaan biasanya hilang setelah setiap fungsi dalam permainan.
Metode ini, seperti apa adanya, dapat membalikkan perhitungan
s[j-1]
, tetapi hanya jika Anda memilikinyas[j]
. Ini berarti Anda hanya dapat menonton tayangan mundur, mulai dari titik di mana Anda memutuskan untuk memutar ulang. Jika Anda ingin memutar ulang mundur dari titik arbitrer, Anda harus mencampur ini dengan metode 4.Untuk catur, ini tidak dapat diterapkan, karena dengan papan yang diberikan dan gerakan sebelumnya, Anda bisa tahu bagian mana yang dipindahkan, tetapi bukan dari mana ia dipindahkan.
Metode 6:
Akhirnya, jika Anda tidak dapat menjamin semua fungsi Anda adalah suntikan, Anda dapat membuat trik kecil untuk melakukannya. Alih-alih setiap fungsi hanya mengembalikan yang baru, status, Anda juga dapat mengembalikannya ke data yang dibuang, seperti:
Di mana
r[j]
data yang dibuang. Dan kemudian buat fungsi terbalik Anda sehingga mereka mengambil data yang dibuang, seperti:Selain
f[j]
danx[j]
, Anda juga harus menyimpanr[j]
untuk setiap fungsi. Sekali lagi, jika Anda ingin dapat mencari, Anda harus menyimpan bookmark, seperti dengan metode 4.Untuk catur, ini akan sama dengan metode 2, tetapi tidak seperti metode 2, yang hanya mengatakan bagian mana yang pergi, Anda juga perlu menyimpan dari mana masing-masing bagian berasal.
Penerapan:
Karena ini berfungsi untuk semua jenis negara, dengan semua jenis fungsi, untuk game tertentu, Anda dapat membuat beberapa asumsi, yang akan membuatnya lebih mudah untuk diterapkan. Sebenarnya, jika Anda menerapkan metode 6 dengan seluruh kondisi permainan, tidak hanya Anda akan dapat memutar ulang data, tetapi juga kembali ke waktu dan melanjutkan bermain dari setiap saat. Itu akan sangat luar biasa.
Alih-alih menyimpan semua kondisi gim, Anda cukup menyimpan minimum yang diperlukan untuk menggambar kondisi tertentu, dan membuat serialisasi data ini setiap jumlah waktu yang tetap. Status Anda akan menjadi serialisasi ini, dan input Anda sekarang akan menjadi perbedaan antara dua serialisasi. Mereka kunci untuk ini bekerja adalah bahwa serialisasi harus sedikit berubah jika negara dunia berubah sedikit juga. Perbedaan ini sepenuhnya tidak dapat dibalik, sehingga penerapan metode 5 dengan bookmark sangat dimungkinkan.
Saya telah melihat ini diimplementasikan dalam beberapa permainan utama, sebagian besar untuk memutar ulang instan data terbaru ketika sebuah peristiwa (frag di fps, atau skor dalam permainan olahraga) terjadi.
Saya harap penjelasan ini tidak terlalu membosankan.
¹ Ini tidak berarti beberapa program bertindak seperti non-deterministik (seperti MS Windows ^^). Sekarang serius, jika Anda dapat membuat program non-deterministik pada komputer deterministik, Anda dapat yakin Anda akan secara bersamaan memenangkan medali Fields, penghargaan Turing dan mungkin bahkan Oscar dan Grammy untuk semua yang bernilai.
sumber
Satu hal yang belum dijawab oleh jawaban lain adalah bahaya mengapung. Anda tidak dapat membuat aplikasi sepenuhnya deterministik menggunakan pelampung.
Menggunakan pelampung, Anda dapat memiliki sistem yang sepenuhnya deterministik, tetapi hanya jika:
Ini karena representasi internal float bervariasi dari satu CPU ke CPU lainnya - paling dramatis antara AMD dan CPU intel. Selama nilainya ada di register FPU, nilainya lebih akurat daripada yang terlihat dari sisi C, sehingga setiap perhitungan menengah dilakukan dengan presisi yang lebih tinggi.
Sudah cukup jelas bagaimana ini akan mempengaruhi AMD vs intel bit - katakanlah seseorang menggunakan floats 80 bit dan 64 lainnya, misalnya - tetapi mengapa persyaratan biner yang sama?
Seperti yang saya katakan, presisi yang lebih tinggi digunakan selama nilai-nilai berada di register FPU . Ini berarti bahwa setiap kali Anda mengkompilasi ulang, optimisasi kompiler Anda dapat bertukar nilai masuk dan keluar dari register FPU, menghasilkan hasil yang sedikit berbeda.
Anda mungkin dapat membantu ini dengan mengatur flag _control87 () / _ controlfp () untuk menggunakan presisi serendah mungkin. Namun, beberapa perpustakaan mungkin juga menyentuh ini (setidaknya beberapa versi d3d melakukannya).
sumber
Simpan status awal generator nomor acak Anda. Kemudian simpan, cap waktu, setiap input (mouse, keyboard, jaringan, apa pun). Jika Anda memiliki game berjaringan, Anda mungkin sudah memiliki ini semua.
Atur ulang RNG dan putar inputnya. Itu dia.
Ini tidak menyelesaikan pemutaran ulang, yang tidak ada solusi umum, selain memutar ulang dari awal secepat mungkin. Anda dapat meningkatkan kinerja untuk ini dengan memeriksa seluruh kondisi game setiap X detik, maka Anda hanya perlu memutar ulang sebanyak itu, tetapi seluruh kondisi game mungkin juga sangat mahal untuk didapatkan.
Khususnya format file tidak masalah, tetapi sebagian besar mesin memiliki cara untuk membuat serialisasi perintah dan menyatakan - untuk jaringan, penyimpanan, atau apa pun. Gunakan saja itu.
sumber
Saya akan memilih menentang replaying deterministik. JAUH lebih sederhana dan JAUH lebih tidak rentan kesalahan untuk menyimpan status setiap entitas setiap 1/1 detik.
Simpan apa yang ingin Anda tampilkan pada pemutaran - jika itu hanya posisi dan tajuk, baiklah, jika Anda juga ingin menampilkan statistik, simpan juga, tetapi secara umum simpan sesedikit mungkin.
Tweak pengodeannya. Gunakan sesedikit mungkin bit untuk semuanya. Replay tidak harus sempurna asalkan terlihat cukup bagus. Bahkan jika Anda menggunakan float untuk, katakanlah, heading, Anda dapat menyimpannya dalam byte dan mendapatkan 256 nilai yang mungkin (ketelitian 1.4º). Itu mungkin cukup atau bahkan terlalu banyak untuk masalah khusus Anda.
Gunakan pengkodean delta. Kecuali jika entitas Anda berteleportasi (dan jika mereka melakukannya, memperlakukan kasing secara terpisah), menyandikan posisi sebagai perbedaan antara posisi baru dan posisi lama - untuk gerakan pendek, Anda dapat melepaskan bit yang jauh lebih sedikit daripada yang Anda perlukan untuk posisi penuh .
Jika Anda ingin mundur cepat, tambahkan kerangka kunci (data lengkap, tanpa delta) setiap N bingkai. Dengan cara ini Anda bisa lolos dengan presisi yang lebih rendah untuk delta dan nilai lainnya, kesalahan pembulatan tidak akan terlalu bermasalah jika Anda mengatur ulang ke nilai "true" secara berkala.
Akhirnya, gzip semuanya :)
sumber
Sulit. Pertama dan yang terutama membaca jawaban Jari Komppa.
Replay yang dibuat di komputer saya mungkin tidak berfungsi di komputer Anda karena hasil float sedikit berbeda. Ini masalah besar.
Tetapi setelah itu, jika Anda memiliki angka acak adalah menyimpan nilai seed di replay. Kemudian muat semua status default dan atur nomor acak ke seed itu. Dari sana Anda bisa merekam keadaan tombol / mouse saat ini dan lamanya waktu seperti itu. Kemudian jalankan semua acara menggunakan itu sebagai input.
Untuk melompati file (yang jauh lebih sulit) Anda harus membuang MEMORY THE. Seperti, di mana setiap unit berada, uang, lamanya waktu berlalu, semua kondisi permainan. Kemudian maju cepat tetapi memutar ulang semuanya kecuali melewatkan rendering, suara dll sampai Anda mencapai tujuan waktu yang Anda inginkan. Ini bisa terjadi setiap menit atau 5 menit tergantung seberapa cepat untuk maju.
Poin utama adalah - Berurusan dengan angka acak - Menyalin input (pemain), dan pemain jarak jauh (s)) - Keadaan membuang untuk melompat-lompat file dan ... - MEMILIKI FLOAT BUKAN HAL-HAL YANG BREAK (ya, saya harus berteriak)
sumber
Saya agak terkejut bahwa tidak ada yang menyebutkan opsi ini, tetapi jika game Anda memiliki komponen multipemain, Anda mungkin telah melakukan banyak kerja keras yang diperlukan untuk fitur ini. Lagipula, apa itu multi-pemain tetapi upaya untuk memutar ulang gerakan orang lain pada waktu yang (sedikit) berbeda di komputer Anda sendiri?
Ini juga memberi Anda manfaat dari ukuran file yang lebih kecil sebagai efek samping, sekali lagi dengan asumsi Anda telah bekerja pada kode jaringan yang ramah bandwidth.
Dalam banyak hal, ini menggabungkan opsi "menjadi sangat deterministik" dan "menyimpan catatan segalanya". Anda masih akan membutuhkan determinisme - jika permainan ulang Anda pada dasarnya adalah bot yang memainkan permainan lagi persis seperti yang Anda mainkan semula, tindakan apa pun yang mereka lakukan yang dapat memiliki hasil acak harus memiliki hasil yang sama.
Format data bisa sesederhana dump lalu lintas jaringan, meskipun saya membayangkan tidak ada salahnya untuk membersihkannya sedikit (Anda tidak perlu khawatir tentang lag pada pemutaran ulang, setelah semua). Anda dapat memainkan ulang hanya sebagian dari permainan dengan menggunakan mekanisme pos pemeriksaan yang telah disebutkan oleh orang lain - biasanya permainan multipemain akan mengirimkan pembaruan permainan sepenuhnya setiap kali, jadi sekali lagi Anda mungkin sudah melakukan pekerjaan ini.
sumber
Untuk mendapatkan file replay sekecil mungkin, Anda perlu memastikan game Anda deterministik. Biasanya ini melibatkan melihat generator nomor acak Anda dan melihat di mana ia digunakan dalam logika permainan.
Anda kemungkinan besar harus memiliki logika permainan RNG dan RNG segalanya untuk hal-hal seperti GUI, efek partikel, suara. Setelah Anda selesai, Anda perlu merekam keadaan awal logika permainan RNG, lalu perintah game dari semua pemain setiap frame.
Untuk banyak game ada tingkat abstraksi antara input dan logika game di mana input diubah menjadi perintah. Misalnya menekan tombol A pada pengontrol menghasilkan "lompatan" perintah digital yang disetel ke true dan logika gim bereaksi terhadap perintah tanpa memeriksa pengontrol secara langsung. Dengan melakukan ini, Anda hanya perlu merekam perintah yang berdampak pada logika game (tidak perlu merekam perintah "Jeda") dan kemungkinan besar data ini akan lebih kecil daripada merekam data pengontrol. Anda juga tidak perlu khawatir tentang keadaan skema kontrol jika pemain memutuskan untuk memetakan kembali tombol.
Menggulung mundur adalah masalah yang sulit menggunakan metode deterministik dan selain menggunakan snapshot dari kondisi permainan dan meneruskan cepat ke titik waktu yang ingin Anda lihat tidak ada banyak yang dapat Anda lakukan selain merekam seluruh kondisi permainan setiap frame.
Di sisi lain, fast-forwarding tentu bisa dilakukan. Selama logika game Anda tidak bergantung pada rendering, Anda dapat menjalankan logika game sebanyak yang Anda inginkan sebelum merender frame baru game. Kecepatan penerusan cepat hanya akan terikat oleh mesin Anda. Jika Anda ingin melompat maju dengan peningkatan besar, Anda harus menggunakan metode snapshot yang sama seperti yang Anda perlukan untuk memutar ulang.
Mungkin bagian terpenting dari penulisan sistem replay yang bergantung pada determinisme adalah merekam aliran data debug. Aliran debug ini berisi snapshot informasi sebanyak mungkin setiap frame (RNG seed, entitas transforms, animations, dll) dan dapat menguji aliran debug yang direkam terhadap keadaan gim selama replay. Ini akan memungkinkan Anda untuk dengan cepat memberi tahu Anda ketidakcocokan di akhir bingkai apa pun yang diberikan. Ini akan menghemat berjam-jam keinginan untuk menarik rambut Anda keluar dari bug non-deterministik yang tidak diketahui. Sesuatu yang sederhana seperti variabel yang tidak diinisialisasi akan mengacaukan semuanya pada jam ke-11.
CATATAN: Jika game Anda melibatkan streaming konten yang dinamis atau Anda memiliki logika permainan pada beberapa utas atau pada inti yang berbeda ... semoga berhasil.
sumber
Untuk mengaktifkan perekaman dan pemutaran ulang, rekam semua peristiwa (yang dibuat pengguna, yang dihasilkan timer, komunikasi yang dihasilkan, ...).
Untuk setiap acara, catat waktu acara, apa yang diubah, nilai sebelumnya, nilai baru.
Nilai yang dihitung tidak perlu direkam kecuali jika penghitungannya acak
(Dalam kasus ini Anda dapat merekam nilai yang dihitung juga, atau mencatat perubahan pada seed setelah setiap perhitungan acak).
Data yang disimpan adalah daftar perubahan.
Perubahan dapat disimpan dalam berbagai format (biner, xml, ...).
Perubahan terdiri dari id entitas, nama properti, nilai lama, nilai baru.
Pastikan sistem Anda dapat memutar ulang perubahan ini (mengakses entitas yang diinginkan, mengubah properti yang diinginkan meneruskan ke status baru atau mundur ke status lama).
Contoh:
Untuk mengaktifkan rewinding / fastforwarding atau perekaman hanya rentang waktu tertentu,
bingkai kunci diperlukan - jika merekam setiap saat, setiap sekarang dan kemudian simpan seluruh kondisi permainan.
Jika merekam hanya rentang waktu tertentu, di awal simpan kondisi awal.
sumber
Jika Anda memerlukan ide tentang bagaimana menerapkan sistem replay Anda, cari google untuk bagaimana menerapkan undo / redo dalam suatu aplikasi. Mungkin jelas bagi sebagian orang, tetapi mungkin tidak bagi semua, bahwa undo / redo secara konseptual sama dengan memutar ulang untuk game. Ini hanya kasus khusus di mana Anda dapat mundur, dan tergantung pada aplikasi, mencari titik waktu tertentu.
Anda akan melihat bahwa tidak ada yang mengimplementasikan undo / redo mengeluh tentang deterministik / non-deterministik, variabel float, atau CPU tertentu.
sumber