Ağa bağlı gerçek zamanlı oyunlar için bir oyun durumu anlık görüntü sistemi nasıl uygulanır?


12

Ağ sınıfım için bir proje olarak basit bir istemci-sunucu gerçek zamanlı çok oyunculu oyun oluşturmak istiyorum.

Gerçek zamanlı çok oyunculu ağ modelleri hakkında çok şey okudum ve istemci ile sunucu ve gecikme-telafi teknikleri arasındaki ilişkileri anlıyorum.

Ne yapmak istiyorum Quake 3 ağ modeline benzer bir şey: temelde, sunucu tüm oyun durumunun bir anlık görüntü depolar; istemcilerden girdi alındığında, sunucu değişiklikleri yansıtan yeni bir anlık görüntü oluşturur. Daha sonra, yeni anlık görüntü ile son görüntü arasındaki farkları hesaplar ve senkronize olabilmeleri için istemcilere gönderir.

Bu yaklaşım benim için gerçekten sağlam görünüyor - istemci ve sunucu sabit bir bağlantıya sahipse, onları senkronize tutmak için sadece minimum miktarda veri gönderilecektir. İstemci senkronizasyondan çıkarsa, tam bir anlık görüntü de istenebilir.

Ancak, anlık görüntü sistemini uygulamak için iyi bir yol bulamıyorum. Tek oyunculu programlama mimarisinden uzaklaşmak ve oyun durumunu nasıl bu şekilde depolayabileceğimi düşünmek gerçekten zor:

  • Tüm veriler mantıktan ayrılır
  • Oyun durumlarının anlık görüntüsü arasındaki farklar hesaplanabilir
  • Oyun varlıkları yine de kod aracılığıyla kolayca değiştirilebilir

Anlık görüntü sınıfı nasıl uygulanır? Varlıklar ve verileri nasıl saklanır? Her istemci varlığın sunucudaki bir kimlikle eşleşen bir kimliği var mı?

Anlık görüntü farklılıkları nasıl hesaplanır?

Genel olarak: bir oyun durumu anlık görüntü sistemi nasıl uygulanır?


4
+1. Bu, tek bir soru için biraz fazla geniş, ancak IMO, bir cevapta kabaca ele alınabilecek ilginç bir konudur.
Kromster

Neden sadece 1 Anlık Görüntü (gerçek dünya) saklamıyorsunuz, gelen tüm değişiklikleri bu normal dünya durumuna kaydetmiyorsunuz ve değişikliği bir liste veya başka bir şeyde saklamıyorsunuz. Daha sonra, değişiklikleri tüm istemcilere gönderme zamanı, listenin içeriğini hepsine gönderip listeyi temizlediğinde, sıfırdan (değişiklikler) başlayın. Belki de bu, 2 anlık görüntüyü depolamak kadar iyi değildir, ancak bu yaklaşımla, 2 anlık görüntünün nasıl hızlılaştırılacağı konusunda algoritmalar hakkında endişelenmenize gerek yoktur.
tkausl

Bunu okudunuz mu: fabiensanglard.net/quake3/network.php - deprem 3 ağ modelinin gözden geçirilmesi, uygulama tartışmasını içerir.
Steven

Ne tür bir oyun inşa etmeye çalışıyorsunuz? Ağ kurulumu büyük ölçüde yaptığınız oyunun türüne bağlıdır. Bir RTS, ağ oluşturma açısından FPS gibi davranmaz.
AturSams

Yanıtlar:


3

İki anlık görüntü örneğini koruyarak anlık görüntü deltasını (önceki senkronize edilmiş durumundaki değişiklikleri) hesaplayabilirsiniz: geçerli olan ve son senkronize edilen olanı.

İstemci girişi geldiğinde mevcut anlık görüntüyü değiştirirsiniz. Ardından, istemcilere delta gönderme zamanı geldiğinde, en son senkronize edilmiş anlık görüntüyü alan teker teker (yinelemeli olarak) hesaplar ve deltayı hesaplar ve serileştirirsiniz. Serileştirme için, sınıfının kapsamındaki her alana benzersiz bir kimlik atayabilirsiniz (küresel durum kapsamının aksine). İstemci ve sunucu genel durum için aynı veri yapısını paylaşmalıdır, böylece istemci belirli bir kimliğin uygulandığını anlar.

Daha sonra, delta hesaplandığında geçerli durumu klonlar ve son senkronize edilmiş hale getirirsiniz, böylece şimdi aynı akım ve son senkronize edilmiş duruma sahipsiniz, ancak farklı durumlara sahipsiniz, böylece mevcut durumu değiştirebilir ve diğerini etkilemezsiniz.

Bu yaklaşımın uygulanması, özellikle yansıma yardımı ile (böyle bir lüksünüz varsa) uygulanması daha kolay olabilir, ancak yansıma kısmını yüksek derecede meşrulaştırsanız bile (çoğu yansıma çağrısını önbelleğe almak için veri şemanızı oluşturarak) yavaş olabilir. Temelde büyük olasılıkla iki durumun iki kopyasını karşılaştırmanız gerektiğinden. Tabii ki karşılaştırmayı nasıl uyguladığınıza ve dilinize bağlıdır. Sabit kodlu karşılaştırıcı ile C ++ 'da hızlı olabilir, ancak çok esnek değil: global durum yapınızdaki herhangi bir değişiklik bu karşılaştırıcının değiştirilmesini gerektirir ve bu değişiklikler ilk proje aşamalarında çok sık görülür.

Başka bir yaklaşım kirli bayrakları kullanmaktır. İstemci girişi her geldiğinde, bunu tek tek genel durum kopyanıza uygular ve karşılık gelen alan (lar) ı kirli olarak işaretler. Ardından, istemcileri senkronize etme zamanı geldiğinde, aynı benzersiz kimlikleri kullanarak kirli alanları (yinelemeli olarak) serileştirirsiniz. (Küçük) dezavantajı bazen kesinlikle gerekenden daha fazla veri göndermenizdir: örneğin int field1başlangıçta 0, daha sonra 1 atandı (ve kirli olarak işaretlendi) ve bundan sonra tekrar 0 atandı (ancak kirli kalıyor). Bunun faydası, büyük hiyerarşik veri yapısına sahip olmanın delta, yalnızca kirli yolları hesaplamak için tamamen analiz etmeniz gerekmemesidir.

Genel olarak, bu görev oldukça karmaşık olabilir, nihai çözümün ne kadar esnek olması gerektiğine bağlıdır. Örneğin Unity3D 5 (yaklaşmakta olan), istemcilerle otomatik olarak senkronize edilmesi gereken verileri belirtmek için öznitelikleri kullanacaktır (çok esnek yaklaşım, alanlarınıza bir özellik eklemek dışında hiçbir şey yapmanız gerekmez) ve ardından yapım sonrası adım. Daha fazla ayrıntı burada.


2

İlk önce, ilgili verilerinizi protokol uyumlu bir şekilde nasıl temsil edeceğinizi bilmeniz gerekir. Bu oyunla ilgili verilere bağlıdır. Örnek olarak bir RTS oyunu kullanacağım.

Ağ oluşturma amacıyla, oyundaki tüm varlıklar numaralandırılır (örn. Manyetikler, birimler, binalar, doğal kaynaklar, yıkılabilirler).

Oyuncuların kendileriyle ilgili verilere sahip olmaları gerekir (örneğin tüm görünür birimler):

  • Yaşıyorlar mı yoksa ölüler mi?
  • Ne tür?
  • Ne kadar sağlık kaldılar?
  • Mevcut konum, dönüş, hız (hız + yön), yakın gelecekte yol ...
  • Aktivite: Saldırı, yürüme, bina, sabitleme, şifa vb.
  • buff / debuff durum efektleri
  • ve muhtemelen mana, kalkanlar gibi diğer istatistikler ve ne değil?

İlk önce oyuncu oyuna girmeden önce tam durumu almalıdır (veya alternatif olarak o oyuncu ile ilgili tüm bilgiler).

Her birimin bir tamsayı kimliği vardır. Nitelikler numaralandırılmıştır ve bu nedenle de integral tanımlayıcılara sahiptir. Birim kimliklerinin 32 bit uzunluğunda olması gerekmez (tutumlu olmadığımızda olabilir). Çok iyi 20 bit (özellikler için 10 bit bırakarak) olabilir. Birimlerin kimliği benzersiz olmalıdır, birim başlatıldığında ve / veya oyun dünyasına eklendiğinde bir sayaç tarafından çok iyi atanabilir (binalar ve kaynaklar hareketsiz bir birim olarak kabul edilir ve haritaya kaynaklara bir kimlik atanabilir yüklendi).

Sunucu geçerli genel durumu depolar. Her oyuncunun en son güncellenen durumu, listson değişikliklerden birine ait bir işaretçi ile temsil edilir (işaretçi henüz o oyuncuya gönderilmeden sonraki tüm değişiklikler). Değişiklikler listmeydana geldiklerinde eklenir . Sunucu son güncellemeyi göndermeyi bitirdikten sonra, liste üzerinde tekrarlamaya başlayabilir: sunucu, oyuncunun işaretçisini liste boyunca kuyruğa taşır, yol boyunca tüm değişiklikleri toplar ve gönderilecek bir arabelleğe yerleştirir oynatıcı (yani protokolün biçimi şu şekilde olabilir: unit_id; attr_id; new_value) Yeni birimler de değişiklik olarak kabul edilir ve tüm özellik değerleriyle alıcı oyunculara gönderilir.

Çöp toplayıcılı bir dil kullanmıyorsanız, geride kalan ve ardından listedeki en eski oyuncu işaretçisini yakalayacak ve yoldaki nesneleri serbest bırakacak tembel bir işaretçi ayarlamanız gerekir. Öncelikli bir yığın içinde hangi oyuncunun en eski olduğunu hatırlayabilir veya tembel işaretçi eşit olana kadar (örneğin, oyuncu işaretçilerinden biriyle aynı öğeyi işaret edene kadar) yineleyebilir ve ücretsiz olabilirsiniz.

Bazı sorular sormadınız ve bence ilginç:

  1. Müşteriler en başta tüm verileri içeren bir anlık görüntü almalı mıdır? Görüş çizgisi dışındaki öğeler ne olacak? RTS oyunlarındaki savaş sisi ne olacak? Tüm verileri gönderirseniz, oynatıcıda bulunmaması gereken verileri görüntülemek için istemci saldırıya uğrayabilir (aldığınız diğer güvenlik önlemlerine bağlı olarak). Yalnızca ilgili verileri gönderirseniz sorun çözülür.
  2. Tüm bilgileri göndermek yerine değişiklik göndermek ne zaman önemlidir? Modern makinelerde mevcut olan bant genişliği düşünüldüğünde, tüm bilgileri göndermek yerine "delta" göndermekten bir şey kazanıyor muyuz?
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.