Bu varlık sistemi nasıl kurulur?


33

Bir FPS için bir varlık sistemi tasarladım. Temelde şöyle çalışır:

GameWorld adında bir "dünya" hedefimiz var. Bu, bir GameObject dizisinin yanı sıra bir ComponentManager dizisini de tutar.

GameObject bir Bileşen dizisine sahiptir. Aynı zamanda gerçekten basit bir olay mekanizması sağlar. Bileşenlerin kendileri, tüm bileşenlere yayınlanan bir öğeye olay gönderebilir.

Bileşen temel olarak bir GameObject'e belirli özellikler veren bir şeydir ve GameObject aslında bunlardan sadece bir kabı olduğundan, bir oyun nesnesiyle yapması gereken her şey Bileşenlerde gerçekleşir. Örnekler ViewComponent, PhysicsComponent ve LogicComponent'dir. Aralarında iletişim gerekliyse, bu olayların kullanılmasıyla yapılabilir.

ComponentManager sadece Component gibi bir arayüzdür ve her Component sınıfı için genellikle bir ComponentManager sınıfı olmalıdır. Bu bileşen yöneticileri, bileşenleri oluşturmaktan ve bunları bir XML dosyası gibi bir şeyden okunan özelliklerle başlatmaktan sorumludur.

ComponentManager ayrıca, harici bir kütüphane kullanacağım (dünyada her şeyi bir kerede yapan) kullanacağım PhysicsComponent gibi bileşenlerin toplu güncellemelerini de gerçekleştirir.

Yapılandırılabilirlik için, bir XML dosyasını veya komut dosyasını okuyacak, dosyada belirtilen bileşenleri oluşturan (aynı zamanda toplu güncellemeler için doğru bileşen yöneticisine bir referans ekleyen) varlıklar için bir fabrika kullanacağım ve sonra onları bir GameObject nesnesine enjekte edin.

Şimdi benim sorunum geliyor: Bunu çok oyunculu oyunlar için kullanmaya çalışacağım. Buna nasıl yaklaşacağımı bilemiyorum.

Birincisi: Müşterilerin en baştan ne gibi kuruluşları olmalı? Tek oyunculu bir motorun hangi varlıkların yaratılacağına nasıl karar vereceğini açıklamakla başlamalıyım.

Seviye editöründe "fırça" ve "varlık" oluşturabilirsiniz. Fırçalar duvarlar, döşemeler ve tavanlar, temelde basit şekiller içindir. Varlıklar size bahsettiğim GameObject. Düzey düzenleyicide varlıklar oluştururken, bileşenlerinin her biri için özellikler belirleyebilirsiniz. Bu özellikler doğrudan varlığın senaryosunda bir kurucu gibi bir şeye aktarılır.

Motorun yükleyeceği seviyeyi kaydettiğinizde, varlıklar listesine ve bunlara ilişkin özelliklere ayrılır. Fırçalar bir "dünya spası" oluşumuna dönüştürülür.

Bu seviyeyi yüklediğinizde, sadece bütün varlıkları teşvik eder. Sesler basit, ha?

Şimdi, varlıkları ağlamak için sayısız sorunla karşılaşıyorum. İlk olarak, müşteriden baştan itibaren hangi varlıklar bulunmalıdır? Hem sunucunun hem de istemcinin seviye dosyasına sahip olduğunu varsayarsak, istemci, yalnızca sunucudaki oyun kurallarının amaçları için olsalar bile, düzeydeki tüm varlıkları daha iyi anlayabilir.

Bir başka olasılık da, müşterinin sunucu hakkında bilgi gönderir göndermez bir işletmeyi teşvik etmesi ve müşterinin yalnızca ihtiyaç duyduğu varlığa sahip olacağı anlamına gelir.

Diğer bir konu ise bilginin nasıl gönderileceğidir. Sunucunun delta sıkıştırma kullanabileceğini düşünüyorum, bu, istemciye her karede bir anlık görüntü göndermek yerine, yalnızca bir şey değiştiğinde yeni bilgiler göndereceği anlamına gelir. Bu, sunucunun şu anda her müşterinin bildiklerini takip etmesi gerektiği anlamına geliyor.

Ve son olarak, ağ motora nasıl enjekte edilmelidir? NetworkConponent olan, ağa bağlanmış olan her varlığa enjekte edilen bir bileşen düşünüyorum. Ancak ağ bileşeni ağdaki hangi değişkenleri ve bunlara nasıl erişileceğini nasıl bilmeli ve nihayet istemcideki karşılık gelen ağ bileşeni ağ değişkenlerinin nasıl değiştirileceğini nasıl bilmelidir?

Buna yaklaşmakta büyük sorun yaşıyorum. Bana yolda yardım edersen çok sevinirim. Bileşen sistemi tasarımının nasıl geliştirileceğine ilişkin ipuçlarına da açığım, bu yüzden bunu önermekten korkmayın.

Yanıtlar:


13

Bu bir tanrının kahrolası (affedilmiş) bir sorusu olan canavarın içinde +1 olan detayları var. Kesinlikle tökezleyenlere yardım edecek kadar.

Sadece fizik verilerini göndermemekle ilgili 2 sentimi eklemek istedim! Gerçekten bunu yeterince vurgulayamam. Bugüne kadar optimize edilmiş olsanız bile, pratikte mikro çarpışmayla zıplayan 40 küre gönderebilir ve sallama odasında kare hızını bile düşürmeyen tam hızda gidebilirsiniz. Konuştuğunuz veri farklılığı olarak da bilinen "delta sıkıştırma / kodlama" işleminize başvurmaktan bahsediyorum. Bu benim ortaya çıkacağım şeye oldukça benziyor.

Ölü Hesap Verme VS Veri Farklılığı: Yeterince farklılar ve gerçekten aynı yöntemleri kullanmıyorlar, yani her ikisini de optimizasyonu daha da artırmak için uygulayabilirsiniz! Not: İkisini birlikte kullanmadım, ikisiyle de çalıştım.

Delta kodlaması veya veri farklılığı: Sunucu, müşterilerin bildikleriyle ilgili verileri taşır ve yalnızca eski veriler ile istemciye nelerin değişmesi gerektiği arasındaki farkı gönderir. örneğin sözde-> bir örnekte, veriler zaten "310 435 210 4000 40" olduğunda "315 435 222 3546 33" verilerini gönderebilirsiniz. Bundan ziyade, oldukça kısa olan "5 0 12 -454 -7" (delta olarak) gönderirsiniz.

Daha iyi örnekler, bundan çok daha fazlasını değiştiren bir şey olabilir; örneğin, şu anda içinde 45 bağlantılı nesne olan bir bağlantılı listem var. 30 tanesini öldürmek istiyorum, o yüzden bunu yapıyorum, sonra herkese yeni paket verilerinin ne olduğunu gönderiyorum, bu zaten böyle şeyler yapmak için inşa edilmediyse sunucuyu yavaşlatacaktı ve çünkü deniyordu. örneğin kendini düzeltmek için. Delta kodlamada, basitçe (sözde) "list.kill 30 at 5" komutunu koyacaktınız ve 5 nesnesinden sonra 30 nesneyi listeden çıkaracak, sonra verileri doğrulayacak, ancak sunucu yerine her istemcide kaldıracaksınız.

Artıları: (Şu anda her birini yalnızca düşünebilir)

  1. Hız: Açıkçası son örneğimde tarif ettim. Önceki örnekten çok daha büyük bir fark olurdu. Genel olarak dürüstçe söyleyemem, hangisinin daha yaygın olacağını tecrübe edemem, çünkü ölü hesaplaşma ile çok daha fazla çalışırım.

Eksileri:

  1. Sisteminizi güncelliyorsanız ve deltada düzenlenmesi gereken bazı veriler eklemek istiyorsanız, bu verileri değiştirmek için yeni işlevler oluşturmanız gerekir! (örneğin daha önceki gibi "list.kill 30 at 5" Oh kahretsin müşteriye eklenmiş bir geri alma yöntemine ihtiyacım var! "list.kill geri al")

Ölü hesaplaşma: Basitçe belirtildiği gibi, burada bir benzetme. Bir yere nasıl gidileceği hakkında bir harita yazıyorum ve sadece genel olarak nereye gideceğimizle ilgili noktaları da dahil ediyorum, çünkü yeterince iyi (binayı bırak, sola dön). Birisi başka bir haritanın cadde isimlerini içerir ve ayrıca sola kaç derece dönebilir, bu gerekli mi? (Yok hayır...)

Ölü hesaplaşma her müşterinin müşteri başına sabit bir algoritmaya sahip olduğu yerdir. Veri, sadece hangi verinin değişmesi gerektiğini ve bunun nasıl yapılacağını söyleyerek değiştirilir. Müşteri verileri kendi başına değiştirir. Bir örnek, oyuncum olmayan bir karaktere sahipsem ama benimle oynayan başka bir kişi tarafından taşınıyorsam, her karede veriyi güncellememeliyim çünkü verilerin çoğu tutarlı!

Diyelim ki karakterim belirli bir yöne hareket ediyor, birçok sunucu, oyuncunun bulunduğu (neredeyse kare başına) ve hareket ettiğini (animasyon nedenleriyle) belirten müşterilere veri gönderecek. Bu çok gereksiz veri! Neden her bir kareyi, birimin nerede olduğunu ve hangi yöne baktığını ve hareket ettiğini neden güncellemeliyim? Basitçe söylemek gerekirse: Yapmam. İstemcileri yalnızca yön değiştiğinde, fiil değiştiğinde (isMoving = true?) Ve nesnenin ne olduğu! Sonra her müşteri nesneyi buna göre hareket ettirir.

Şahsen bu sağduyu taktiğidir. Uzun zaman önce gelmekte akıllı olduğumu düşündüğüm bir şeydi, bu her zaman kullanıldı.

Cevaplar

Dürüst olmak gerekirse, James'in gönderisini okuyun ve veriler hakkında söylediklerimi okuyun. Evet kesinlikle delta-kodlama kullanmalısınız, fakat ölü hesaplaşma da düşünün.

Şahsen, sunucudan bu konuda bilgi aldığında (önerdiğiniz bir şey), istemcideki verileri başlatırdım.

Yalnızca değişebilen nesnelerin, ilk etapta düzenlenebilir olduğu belirtilmelidir. Bileşen ve varlık sisteminiz aracılığıyla bir nesnenin ağ verilerinin olması gerektiğini dahil etme fikrinizi seviyorum! Zekice ve iyi çalışması gerekiyor. Ancak, hiçbir zaman ağ oluşturma yöntemlerini fırçalar (veya kesinlikle tutarlı olan herhangi bir veri) vermemelisiniz. Buna ihtiyaçları yok, çünkü değişemeyecek bir şey (müşteriden müşteriye olan).

Bir kapıya benzer bir şeyse, ağ verilerini verirdim, ancak açık olup olmadığına dair yalnızca bir boolean, sonra açıkçası ne tür bir nesne olduğu. Müşteri nasıl değiştirileceğini bilmelidir, örn. Açık, kapat, her müşteri hepsini kapatması gerektiğini alır, böylece boolean verilerini değiştirirsiniz, sonra kapanacak kapıyı canlandırırsınız.

Hangi değişkenleri ağa bilmesi gerektiğine gelince, gerçekten bir SUB nesnesi olan bir bileşene sahip olabilir ve ona ağ oluşturmak istediğiniz bileşenleri verebilirim. Başka bir fikir, sadece sahip olmak AddComponent("whatever")değil , aynı zamanda AddNetComponent("and what have you")kişisel olarak daha akıllı göründüğü için de .


Bu gülünç derecede uzun bir cevap! Bunun için çok üzgünüm. Sadece az miktarda bilgi vermeyi düşündüğüm için ve sonra bazı şeyler hakkında 2 sent. Bu yüzden anlıyorum ki çoğunun not alması biraz gereksiz olabilir.
Joshua Hedges

3

Bir yorum yazacaktı, ancak bunun bir cevap için yeterli bilgi olabileceğine karar verdi.

Birincisi, cevabını değerlendirmek için tonlarca ayrıntı içeren bu kadar güzel yazılmış bir soru için +1.

Veri yüklemesi için müşterinin dünyayı dünya dosyasından yüklemesini sağlayacağım. Varlıklarınızda veri dosyasından gelen kimlikleri varsa, varsayılan olarak onları da yüklerim, böylece ağ sisteminiz hangi nesnelerden bahsettiğini bilmek için onlara başvurabilir. Aynı başlangıç ​​verilerini yükleyen herkes, bu nesneler için hepsinin aynı kimliklere sahip olduğu anlamına gelmelidir.

İkincisi, bir NetworkComponent bileşeni yapmayın, çünkü bu diğer mevcut bileşenlerde (kopya, fizik, animasyon ve benzerleri arasında gönderilecek yaygın şeyler) verileri çoğaltmaktan başka bir şey yapmaz. Kendi adınızı kullanmak için bir NetworkComponentManager yapmak isteyebilirsiniz. Bu, sahip olduğunuz diğer Bileşen'den ComponentManager ilişkisine biraz bağlı olacaktır, ancak ağa bağlı bir oyuna başladığınızda ve ağ oluşturma özelliği olan herhangi bir bileşen türü olduğunda, bu bilgileri toplayabilmesi için yöneticiye vermesi durumunda başlatılabilir. ve gönder. Verileri paketlemek için kullanabileceğiniz bir tür serileştirme / seri kaldırma mekanizmasına sahipseniz, Kaydet / Yükle işlevinizin kullanılabileceği yer,

Sorunuz ve bilgi düzeyiniz göz önüne alındığında daha fazla ayrıntıya girmem gerektiğini düşünmüyorum, ancak bir şey açık değilse lütfen bir yorum gönderin; bu sorunun cevabını güncelleyeceğim.

Bu yardımcı olur umarım.


Öyleyse, demek istediğin, ağa bağlanması gereken bileşenlerin böyle bir arabirim kullanması gerektiğidir ?: void SetNetworkedVariable (string name, NetworkedVariable value); NetworkedVariable GetNetworkedVariable (dize adı); NetworkedVariable'ın enterpolasyon ve diğer ağ işleri için kullanıldığı yerlerde. Bununla birlikte, hangi bileşenleri uygulayanın nasıl tanımlanacağını bilmiyorum. Çalışma zamanı türü kimliğini kullanabilirim, ancak bu bana çok çirkin geliyor.
Carter
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.