Apa yang sebenarnya bergerak di pelari tak berujung?

107

Misalnya permainan Flappy Bird yang terkenal, atau apa pun yang benar-benar menyortir, adalah pemain (dalam hal ini burung, atau kamera mana pun yang Anda suka) bergerak maju atau seluruh dunia bergerak mundur (burung hanya mengubah posisi Y dan memiliki posisi X konstan)?

Lendrit Ibrahimi
sumber
1
Kami telah menghapus banyak komentar di luar topik dari pertanyaan ini dan jawabannya. Kami juga telah memindahkan beberapa diskusi masuk akal untuk mengobrol, dalam beberapa kasus berulang kali. Meskipun demikian, beberapa pengguna merasa perlu mengabaikan fakta bahwa komentar tidak untuk diskusi panjang dan berlanjut. Karena itu, pertanyaan ini sementara dikunci.
Josh

Jawaban:

146

Saya sedikit tidak setuju dengan jawaban Philipp ; atau setidaknya dengan bagaimana dia mempresentasikannya. Ini memberi kesan bahwa menggerakkan dunia di sekitar pemain mungkin ide yang lebih baik; padahal justru sebaliknya. Jadi, inilah jawaban saya sendiri ...


Kedua opsi dapat bekerja, tetapi umumnya merupakan ide yang buruk untuk "membalikkan fisika" dengan menggerakkan dunia di sekitar pemain daripada pemain di seluruh dunia.


Kehilangan / pemborosan kinerja:

Dunia biasanya akan memiliki banyak objek; banyak jika tidak sebagian besar, statis atau tidur. Pemain akan memiliki satu, atau relatif sedikit, objek. Memindahkan seluruh dunia di sekitar pemain, berarti memindahkan semua yang ada di adegan kecuali pemain. Objek statis, objek dinamis tidur, objek dinamis aktif, sumber cahaya, sumber suara, dll; semua harus dipindahkan.

Itu (jelas) jauh lebih mahal daripada hanya memindahkan apa yang sebenarnya bergerak (pemain, dan mungkin beberapa hal lagi).


Maintabilitas & Ekstensibilitas:

Menggerakkan dunia di sekitar pemain membuat dunia (dan semua yang ada di dalamnya) menjadi titik di mana hal-hal yang paling aktif terjadi. Setiap bug atau perubahan dalam sistem berarti bahwa, berpotensi, semuanya berubah. Ini bukan cara yang baik untuk melakukan sesuatu; Anda ingin bug / perubahan terisolasi sebanyak mungkin, sehingga Anda tidak mendapatkan perilaku tak terduga di suatu tempat yang belum Anda lakukan perubahan.

Ada juga banyak masalah lain dengan pendekatan ini. Sebagai contoh, itu mematahkan banyak asumsi tentang bagaimana segala sesuatu seharusnya bekerja di dalam mesin. Anda tidak akan dapat menggunakan dinamis RigidBodyuntuk apa pun selain pemain, misalnya; sebagai objek dengan lampiran yang RigidBodytidak disetel ke kinematik akan berperilaku tak terduga ketika mengatur posisi / rotasi / skala (yang akan Anda lakukan setiap frame, untuk setiap objek dalam adegan, kecuali pemain 😨)


Jadi jawabannya adalah memindahkan pemain saja!

Ya ... ya dan tidak . Seperti yang disebutkan dalam jawaban Philipp, dalam jenis game pelari tak terbatas (atau game apa pun dengan area eksplorer besar yang mulus), terlalu jauh dari asalnya pada akhirnya akan memperkenalkan FPPE yang terlihat ( Floating-Point Precision Errors ), dan lebih jauh lagi, akhirnya, melimpahi tipe numerik, baik menyebabkan game Anda crash, atau, pada dasarnya, membuat asap-dunia permainan retak ... Pada steroid! 😵 (karena pada titik ini, FPPEs akan membuat gim tersebut sudah berada pada celah "normal")


The aktual solusi:

Jangan melakukan keduanya dan lakukan keduanya! Anda harus menjaga dunia tetap statis, dan menggerakkan pemain di sekitarnya. Tapi "re-root" baik , pemain dan dunia, ketika pemain mulai terlalu jauh dari akar (posisi [0, 0, 0]) adegan.

Jika Anda mempertahankan posisi relatif barang (pemain dengan dunia di sekitarnya), dan melakukan proses ini dalam satu pembaruan bingkai, pemain (sebenarnya) bahkan tidak akan menyadarinya!

Untuk melakukannya, Anda memiliki dua opsi utama:

  1. Pindahkan pemain ke akar adegan, dan pindahkan potongan dunia ke posisi baru itu relatif terhadap pemain.
  2. Pikirkan dunia seperti kotak; pindahkan bagian grid yang pemain mainkan ke root, dan pindahkan pemain ke posisi baru itu relatif terhadap bagian grid itu.

Berikut adalah contoh dari proses ini dalam tindakan


Tapi, seberapa jauh terlalu jauh?

Jika Anda melihat kode sumber Unity, mereka menggunakan 1e-5( 0.00001) sebagai dasar untuk mempertimbangkan dua nilai titik-mengambang "sama", di dalam Vector2dan Vector3(tipe-data yang bertanggung jawab atas posisi objek, rotasi dan skala [euler-]). Karena kehilangan presisi floating-point terjadi jauh dari nol, aman untuk mengasumsikan bahwa apa pun di bawah 1e+5( 100000) unit yang jauh dari tempat asal / asal aman untuk digunakan.

Tapi! Sejak...

  1. Lebih tepat membuat sistem untuk menangani proses rooting ulang ini secara otomatis.
  2. Apa pun gim Anda, tidak perlu ada "bagian" dunia yang berdekatan untuk menjadi 100000 unit (meter [?]).

... maka mungkin ide yang bagus untuk melakukan root ulang lebih cepat / lebih sering daripada tanda 100000 unit. Contoh video yang saya berikan tampaknya melakukannya setiap 1000 unit atau lebih, misalnya.

XenoRo
sumber
2
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan . Silakan gunakan obrolan itu daripada mengirim lebih banyak komentar di sini.
Alexandre Vaillancourt
> Itu (jelas) jauh lebih mahal daripada hanya memindahkan apa yang sebenarnya bergerak <Apakah itu? Saat merender, Anda tetap harus melakukan perkalian matriks dengan matriks kamera pada semua objek, memiliki kamera yang diperbaiki pada identitas akan lebih murah dengan cara ini.
Matsemann
Saat itu ketika pengembangan game menjadi pengembangan persatuan ...
LJ ᛃ
@ Matemann - Jika Anda pergi untuk mengobrol, Anda akan memperhatikan ini bukan poin baru. Seperti yang sudah dijelaskan, render adegan NOT-EQUALS; mereka adalah subyek yang berbeda dan hampir sepenuhnya independen dan jalur pipa. Melakukan inversi kerangka referensi dengan cara objek diposisikan dalam adegan akan berarti Anda akan memiliki proses yang lebih berat di kedua ujungnya , bukan hanya dalam render. --- Juga, bahkan jika kinerja sama-sama diperdagangkan seperti yang Anda katakan, semua masalah lain dengan inversi masih akan tetap tidak terpecahkan, membuat pendekatan itu masih lebih buruk daripada me-rooting ulang.
XenoRo
@ LJ ᛃ Pertanyaan dibuat tentang persatuan secara khusus (lihat tag OP sebelum suntingan [defacing] D. Everhard], dan jawabannya disesuaikan untuk mencerminkan hal itu . --- Tapi inilah bagian yang terbaik: Baik itu Persatuan, Unreal, CryEngine, Source, dll ... Mesin terbaik dan / atau paling populer bekerja dengan cara ini (dan mereka melakukannya karena suatu alasan), jadi jawabannya bukan hanya sangat valid secara umum, tetapi poin yang dibuat masih di puncak akurasi . Secara umum, jika Anda membalikkan kerangka referensi fisika, Anda dapat mengalami masalah, khususnya pada objek dinamis.
XenoRo
89

Kedua opsi berfungsi.

Tetapi jika Anda ingin pelari tanpa akhir menjadi benar-benar tidak ada habisnya, Anda harus menjaga stasioner pemain dan menggerakkan dunia. Kalau tidak, Anda pada akhirnya akan mencapai batas variabel yang Anda gunakan untuk menyimpan posisi-X. Integer pada akhirnya akan meluap dan variabel floating point akan menjadi semakin kurang akurat yang akan membuat gameplay menjadi glitchy setelah beberapa saat. Tetapi Anda dapat menghindari masalah ini dengan menggunakan tipe yang cukup besar sehingga tidak seorang pun akan menghadapi masalah ini dalam rentang waktu yang bisa dimainkan dalam satu sesi (ketika seorang pemain bergerak 1000 piksel per detik, bilangan bulat 32 bit akan meluap setelah 49 hari).

Jadi, lakukan apa pun yang secara konseptual terasa lebih intuitif bagi Anda.

Philipp
sumber
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Josh
1
Saya tidak berpikir itu memecahkan apa pun. Alih-alih menjaga posisi pemain (dan kamera), Anda mempertahankan posisi gulir dunia - bagaimana bedanya?
Agent_L
1
@ Agg_L Secara umum, dunia dihasilkan sedikit demi sedikit, dan setiap bagian memiliki posisi X / Y. Ketika bagian mendapat di belakang pemain dengan cukup (misalnya offscreen), itu dihapus, dan mereka hanya menghasilkan jumlah tertentu di muka, sehingga mereka selalu dalam kisaran yang relatif kecil - satu pertandingan yang saya buat tidak pernah memiliki koordinat yang lebih besar dari ~ 1000 atau kurang dari 0, meskipun menjadi pelari tak terbatas, yang dihasilkan secara acak, karena itu - saya melacak posisi pemain di setiap chunk, dan setiap indeks chunk dalam urutan.
Nic Hartley
Saya tidak membeli konsep "variable overflow". Jika pemain tidak bergerak, maka "latar belakang" akan diimbangi secara negatif dengan jumlah yang sama dengan yang akan diimbangi pemain jika bergerak. Kedua kasus akan mengalami masalah yang sama persis (dalam arah yang berlawanan), dan keduanya akan membutuhkan penanganan khusus pada batas luapan.
pemboros
2
pada 1000 piksel per detik akan dibutuhkan lebih dari 586 juta tahun untuk melampaui integer 64 bit. Ini hanya masalah jika Anda menggunakan tipe yang sangat kecil seperti bilangan bulat 16 bit.
Lyndon White
38

Membangun jawaban XenoRo , alih-alih metode rooting ulang yang mereka gambarkan, orang bisa melakukan yang berikut:

Buat penyangga bundar bagian-bagian dari peta tak terbatas Anda yang dihasilkan, yang karakter Anda bergerak dengan posisi diperbarui dengan modulo aritmatika (sehingga Anda hanya berjalan di sekitar penyangga melingkar). Mulailah mengganti bagian-bagian buffer Anda segera setelah karakter Anda meninggalkan bagian. Persamaan pembaruan pemain akan menjadi seperti:

player.position = (player.position + player.velocity) % worldBuffer.width;

di sini adalah contoh gambar dari apa yang saya bicarakan:

masukkan deskripsi gambar di sini

Berikut adalah contoh dari apa yang terjadi pada pembungkus di bagian akhir.

masukkan deskripsi gambar di sini

Dengan metode ini Anda tidak pernah mengalami kesalahan presisi, tetapi Anda mungkin perlu membuat buffer yang sangat besar jika Anda berniat melakukan ini dalam 3d dengan jarak pandang sangat jauh (karena Anda masih harus dapat melihat ke depan sendiri) ). Jika flappy bird ukuran chunk Anda mungkin hanya akan sebesar yang diperlukan untuk memegang satu adegan untuk satu layar, dan buffer Anda bisa sangat kecil.

Perhatikan bahwa Anda akan mulai mendapatkan hasil berulang dengan APA PUN , dan jumlah maksimum urutan berulang dari generasi PRNG biasanya kurang dari panjang pow (2, jumlah bit yang digunakan secara internal), dengan merzenne twister ini bukan banyak masalah, karena menggunakan 2.5k keadaan internal, tetapi jika Anda menggunakan varian kecil, Anda memiliki iterasi 2 ^ 127-1 maks sebelum diulang (atau lebih buruk), ini masih merupakan jumlah yang sangat besar secara astronomi . Anda dapat memperbaiki masalah periode berulang bahkan jika PRNG Anda memiliki periode singkat melalui fungsi pencampuran longsoran yang baik untuk seed (saat Anda menambahkan lebih banyak status secara implisit) berulang kali.

opa
sumber
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Josh
15

Seperti yang sudah ditanyakan dan diterima, itu benar-benar tergantung pada ruang lingkup dan gaya permainan Anda, tetapi karena tidak disebutkan: FlappyBird memindahkan penghalang di layar, daripada pemain di seluruh dunia.

Seorang spawner adalah instantiating objek dari layar dengan kecepatan tetap di Vector2.leftarah

Stephan
sumber
3
Itu bekerja untuk FlappyBird karena ini adalah permainan yang sangat sederhana. Seperti yang disebutkan dalam jawaban saya, teknik yang sama, walaupun masih memungkinkan , akan sangat bermasalah pada hal yang lebih kompleks (dengan lebih banyak objek / detail dunia), seperti Subway Surfer.
XenoRo
15
Saya setuju, dan jawaban Anda sangat teliti. Saya hanya tidak melihat ada yang menyebutkan metode mengepakkan burung yang sebenarnya digunakan, jadi saya pikir saya akan menambahkannya.
Stephan