Bagaimana cara jaringan sistem entitas ini?

33

Saya telah merancang sistem entitas untuk FPS. Ini pada dasarnya bekerja seperti ini:

Kami memiliki objek "dunia", yang disebut GameWorld. Ini menampung array dari GameObject, serta array dari ComponentManager.

GameObject memegang larik Komponen. Ini juga menyediakan mekanisme acara yang sangat sederhana. Komponen itu sendiri dapat mengirim acara ke entitas, yang disiarkan ke semua komponen.

Komponen pada dasarnya adalah sesuatu yang memberikan GameObject properti tertentu, dan karena GameObject sebenarnya hanya sebuah wadah dari mereka, segala sesuatu yang berkaitan dengan objek permainan terjadi di Komponen. Contohnya termasuk ViewComponent, PhysicsComponent dan LogicComponent. Jika komunikasi di antara mereka diperlukan, itu bisa dilakukan melalui penggunaan acara.

ComponentManager hanya antarmuka seperti Component, dan untuk setiap kelas Component, umumnya harus ada satu kelas ComponentManager. Manajer komponen ini bertanggung jawab untuk membuat komponen dan menginisialisasi komponen tersebut dengan properti yang dibaca dari sesuatu seperti file XML.

ComponentManager juga menangani pembaruan komponen secara massal, seperti PhysicsComponent di mana saya akan menggunakan perpustakaan eksternal (yang melakukan semuanya di dunia sekaligus).

Untuk konfigurasi, saya akan menggunakan pabrik untuk entitas yang akan membaca file XML atau skrip, membuat komponen yang ditentukan dalam file (yang juga menambahkan referensi ke dalamnya di manajer komponen yang tepat untuk pembaruan massal), dan lalu menyuntikkannya ke objek GameObject.

Sekarang masalah saya: Saya akan mencoba menggunakan ini untuk game multi-pemain. Saya tidak tahu bagaimana mendekati ini.

Pertama: Entitas apa yang harus dimiliki klien sejak awal? Saya harus mulai dengan menjelaskan bagaimana mesin pemain tunggal akan menentukan entitas apa yang akan dibuat.

Di editor tingkat Anda dapat membuat "kuas" dan "entitas". Kuas untuk hal-hal seperti dinding, lantai dan langit-langit, pada dasarnya bentuk sederhana. Entitas adalah GameObject yang telah saya ceritakan. Saat membuat entitas di editor level, Anda dapat menentukan properti untuk masing-masing komponen itu. Properti ini diteruskan langsung ke sesuatu seperti konstruktor dalam skrip entitas.

Ketika Anda menyimpan level untuk memuat mesin, itu didekomposisi menjadi daftar entitas dan properti terkait. Kuas diubah menjadi entitas "worldspawn".

Ketika Anda memuat level itu, itu hanya instanciates semua entitas. Kedengarannya sederhana, eh?

Sekarang, untuk jaringan entitas saya mengalami banyak masalah. Pertama, entitas apa yang harus ada pada klien dari awal? Dengan asumsi bahwa baik server dan klien memiliki file level, klien mungkin juga dapat mem-instanci semua entitas di level tersebut, bahkan jika mereka ada di sana hanya untuk keperluan aturan permainan di server.

Kemungkinan lain adalah bahwa klien membuat entitas segera setelah server mengirim informasi tentangnya, dan itu berarti bahwa klien hanya akan memiliki entitas yang dibutuhkan.

Masalah lain adalah bagaimana mengirim informasi. Saya pikir server dapat menggunakan delta-kompresi, artinya hanya mengirim informasi baru ketika sesuatu berubah, daripada mengirim snapshot ke klien di setiap frame. Padahal itu berarti server harus melacak apa yang diketahui setiap klien saat ini.

Dan akhirnya, bagaimana jaringan disuntikkan ke mesin? Saya sedang memikirkan komponen, NetworkComponent, yang disuntikkan ke setiap entitas yang seharusnya jaringan. Tetapi bagaimana seharusnya komponen jaringan tahu variabel apa yang ada di jaringan, dan bagaimana mengaksesnya, dan akhirnya bagaimana komponen jaringan yang sesuai pada klien harus tahu cara mengubah variabel jaringan?

Saya mengalami masalah besar dalam mendekati ini. Saya akan sangat menghargai jika Anda membantu saya dalam perjalanan. Saya terbuka untuk tips tentang bagaimana meningkatkan desain sistem komponen juga, jadi jangan takut menyarankan itu.

Tukang gerobak
sumber

Jawaban:

13

Ini adalah binatang terkutuk (pengampunan) dari sebuah pertanyaan dengan banyak detail +1 di sana. Cukup jelas untuk membantu orang yang menemukannya.

Saya hanya sebagian besar ingin menambahkan 2 sen saya tentang tidak mengirim data fisika !! Jujur saya tidak bisa cukup menekankan hal ini. Bahkan jika Anda telah mengoptimalkannya sejauh ini sehingga Anda dapat secara praktis mengirim 40 bola yang terpental dengan tabrakan mikro dan itu bisa melaju dengan kecepatan penuh di ruang guncangan yang bahkan tidak mengurangi laju bingkai. Saya merujuk untuk melakukan "delta-compression / encoding" Anda juga dikenal sebagai pembedaan data yang Anda diskusikan. Ini sangat mirip dengan apa yang akan saya ungkapkan.

Dead Reckoning VS Data Differencing: Mereka cukup berbeda dan benar-benar tidak menggunakan metode yang sama, artinya Anda dapat menerapkan keduanya untuk meningkatkan optimasi lebih jauh! Catatan: Saya belum pernah menggunakan keduanya bersama-sama, tetapi telah bekerja dengan keduanya.

Delta encoding atau data-differencing: Server membawa data tentang apa yang klien ketahui, dan hanya mengirim perbedaan antara data lama dan apa yang harus diubah ke klien. misalnya pseudo-> dalam satu contoh Anda mungkin mengirim data "315 435 222 3546 33" ketika data sudah "310 435 210 4000 40" Beberapa hanya sedikit berubah, dan satu tidak berubah sama sekali! Daripada itu, Anda akan mengirim (dalam delta) "5 0 12 -454 -7" yang jauh lebih pendek.

Contoh yang lebih baik mungkin adalah sesuatu yang berubah jauh lebih jauh dari itu misalnya, katakanlah saya memiliki daftar tautan dengan 45 objek tertaut di dalamnya sekarang. Saya ingin membunuh 30 dari mereka, jadi saya melakukan itu, kemudian kirim ke semua orang apa paket data baru itu, yang akan memperlambat server jika belum dibangun untuk melakukan hal-hal seperti ini, dan itu terjadi karena sedang mencoba untuk memperbaiki sendiri misalnya. Dalam pengkodean delta, Anda cukup menaruh (pseudo) "list.kill 30 at 5" dan itu akan menghapus 30 objek dari daftar setelah yang ke 5 lalu mengotentikasi data, tetapi pada setiap klien daripada server.

Pro: (Hanya bisa memikirkan satu dari masing-masing sekarang)

  1. Kecepatan: Jelas dalam contoh terakhir yang saya jelaskan. Perbedaannya akan jauh lebih besar dari contoh sebelumnya. Secara umum saya tidak dapat dengan jujur ​​mengatakan dari pengalaman yang mana dari yang akan lebih umum, karena saya bekerja lebih banyak dengan perhitungan mati

Cons:

  1. Jika Anda memperbarui sistem Anda dan ingin menambahkan lebih banyak data yang harus diedit melalui delta, Anda harus membuat fungsi baru untuk mengubah data itu! (mis. seperti sebelumnya "list.kill 30 at 5" Oh sial aku butuh metode undo ditambahkan ke klien! "list.kill urungkan")

Perhitungan mati: Singkatnya, ini analogi. Saya sedang menulis peta untuk seseorang tentang cara menuju ke lokasi, dan saya hanya memasukkan titik-titik ke mana harus pergi secara umum, karena itu cukup baik (berhenti di gedung, belok kiri). Peta seseorang termasuk nama jalan dan juga berapa derajat untuk belok kiri, apakah itu perlu sama sekali? (Tidak...)

Perhitungan mati adalah di mana setiap klien memiliki algoritma yang konstan per klien. Data cukup banyak diubah dengan mengatakan data mana yang perlu diubah, dan bagaimana melakukannya. Klien mengubah data sendiri. Contohnya adalah jika saya memiliki karakter yang bukan pemain saya, tetapi sedang dipindahkan oleh orang lain yang bermain dengan saya, saya tidak harus memperbarui data setiap frame karena banyak data konsisten!

Katakanlah saya memiliki karakter saya bergerak ke beberapa arah, banyak server akan mengirim data ke klien yang mengatakan (hampir per frame) di mana pemain berada, dan bahwa itu bergerak (untuk alasan animasi). Itu banyak data yang tidak perlu! Kenapa saya harus memperbarui setiap frame, di mana unit berada dan ke arah mana ia menghadap DAN bahwa itu bergerak? Sederhananya: saya tidak. Anda memperbarui klien hanya ketika arahnya berubah, ketika kata kerjanya berubah (isMoving = true?) Dan apa objeknya! Kemudian setiap klien akan memindahkan objek sesuai.

Secara pribadi ini adalah taktik akal sehat. Itu adalah sesuatu yang saya pikir saya pandai muncul sejak lama, yang ternyata digunakan sepanjang waktu.

Jawaban

Sejujurnya, baca posting James dan baca apa yang saya katakan tentang data. Ya, Anda harus paling pasti menggunakan delta-encoding, tetapi pikirkan juga menggunakan perhitungan mati.

Secara pribadi saya akan instantiate data pada klien, ketika menerima informasi tentang hal itu dari server (sesuatu yang Anda sarankan).

Hanya objek yang dapat berubah yang harus dicatat sebagai yang dapat diedit sejak awal, bukan? Saya suka ide Anda termasuk bahwa suatu objek harus memiliki data jaringan, melalui komponen dan sistem entitas Anda! Ini pintar, dan seharusnya bekerja dengan baik. Tetapi Anda tidak boleh memberikan kuas (atau data apa pun yang sangat konsisten) metode jaringan apa pun. Mereka tidak membutuhkannya, karena itu adalah sesuatu yang bahkan tidak dapat diubah (dari klien ke klien).

Jika itu sesuatu seperti pintu, saya akan memberikannya data jaringan tetapi hanya boolean apakah itu terbuka atau tidak, maka jelas jenis objeknya. Klien harus tahu cara mengubahnya, misalnya terbuka, tutup, setiap klien menerima bahwa mereka semua harus menutupnya, jadi Anda mengubah data boolean, lalu menghidupkan pintu yang akan ditutup.

Adapun bagaimana ia harus tahu variabel apa ke jaringan, saya mungkin memiliki komponen yang benar-benar objek SUB, dan memberikannya komponen yang ingin Anda jaringan. Gagasan lain adalah untuk tidak hanya memiliki AddComponent("whatever")tetapi juga AddNetComponent("and what have you")hanya karena kedengarannya lebih pintar secara pribadi.

Joshua Hedges
sumber
Ini jawaban yang sangat panjang! Saya sangat menyesal tentang itu. Seperti yang saya maksudkan hanya menyediakan sedikit pengetahuan dan kemudian 2 sen saya tentang beberapa hal. Jadi saya mengerti bahwa banyak dari itu mungkin sedikit tidak perlu dicatat.
Joshua Hedges
3

Akan menulis komentar tetapi memutuskan ini mungkin informasi yang cukup untuk jawaban.

Pertama, +1 untuk pertanyaan yang ditulis dengan begitu baik dengan banyak detail untuk menilai jawabannya.

Untuk pemuatan data, saya ingin klien memuat dunia dari file dunia. Jika entitas Anda memiliki Id di dalamnya yang berasal dari file data maka saya juga akan memuatnya secara default sehingga sistem jaringan Anda bisa merujuk mereka untuk mengetahui objek mana yang dibicarakannya. Setiap orang yang memuat data awal yang sama harus berarti mereka semua memiliki Id yang sama untuk objek-objek tersebut.

Kedua, jangan membuat komponen NetworkComponent karena ini tidak akan melakukan apa-apa selain mereplikasi data dalam komponen lain yang ada (fisika, animasi dan sejenisnya adalah beberapa hal umum yang harus dikirim). Untuk menggunakan penamaan Anda sendiri, Anda mungkin ingin membuat NetworkComponentManager. Ini akan sedikit tidak aktif dari hubungan Component to ComponentManager lain yang Anda miliki tetapi ini dapat dipakai saat Anda memulai permainan jaringan dan memiliki semua jenis komponen yang memiliki aspek jaringan untuk mereka memberikan data mereka kepada manajer sehingga dapat mengemasnya dan kirimkan. Di sinilah fungsi Simpan / Muat Anda dapat digunakan jika Anda memiliki semacam mekanisme serialisasi / deserialisasi yang juga dapat Anda gunakan untuk mengemas data, seperti yang disebutkan,

Mengingat pertanyaan dan tingkat informasi Anda, saya pikir saya tidak perlu membahas lebih detail, tetapi jika ada yang tidak jelas, silakan kirim komentar dan saya akan memperbarui jawaban untuk mengatasinya.

Semoga ini membantu.

James
sumber
Jadi, apa yang Anda katakan adalah bahwa komponen yang harus jaringan harus mengimplementasikan beberapa jenis antarmuka seperti ini ?: void SetNetworkedVariable (nama string, nilai NetworkedVariable); NetworkedVariable GetNetworkedVariable (nama string); Di mana NetworkedVariable digunakan untuk keperluan interpolasi dan hal-hal jaringan lainnya. Saya tidak tahu bagaimana mengidentifikasi komponen mana yang mengimplementasikan ini. Saya bisa menggunakan identifikasi tipe runtime, tapi itu tampak jelek bagi saya.
Carter