Gerçek Zamanlı Strateji oyunları için ağ oluşturma


16

Aldığım bir bilgisayar bilimleri kursu için gerçek zamanlı bir strateji oyunu geliştiriyorum. Bunun en zor yönlerinden biri istemci-sunucu ağı ve senkronizasyonu gibi görünüyor. Bu konuyu okudum ( 1500 okçu dahil ), ancak diğer modellerin aksine (örneğin LAN üzerinden) bir istemci-sunucu yaklaşımı kullanmaya karar verdim.

Bu gerçek zamanlı strateji oyunu bazı problemlerle birlikte gelir. Neyse ki, oyuncunun yaptığı her eylem belirleyicidir. Ancak, planlanan aralıklarla gerçekleşen olaylar vardır. Örneğin, oyun fayanslardan oluşur ve bir oyuncu bir fayans aldığında, o karo üzerindeki bir değer olan 'enerji seviyesi', alındıktan sonra saniyede bir artar. Bu benim kullanım durumumu haklı göstermesi gereken çok hızlı bir açıklama.

Şu anda ince istemcileri yapıyorum, sadece sunucuya paketler gönderiyor ve yanıt bekliyorum. Ancak, bazı sorunlar var.

Oyuncular arasındaki oyunlar oyunsonuna dönüştüğünde, genellikle saniyede 50'den fazla olay olur (önceden planlanmış olaylar nedeniyle, daha önce açıklanır, birikir) ve senkronizasyon hataları ortaya çıkmaya başlar. Benim en büyük sorunum, istemciler arasındaki durumdaki küçük bir sapmanın bile, müşterilerin aldıkları farklı kararlar anlamına gelebilmesidir, bu da kartopunun tamamen ayrı oyunlara dönüşmesidir. Başka bir sorun (şu anda o kadar önemli değil), gecikme olduğu ve birisinin sonucu görmek için harekete geçtikten birkaç saniye sonra bile birkaç milisaniye beklemesi gerektiğidir.

Bunu son kullanıcı için daha kolay, daha hızlı ve daha eğlenceli hale getirmek için hangi stratejileri ve algoritmaları kullanabileceğimi merak ediyorum. Bu, oyun başına birkaç oyuncu ile birlikte saniyede yüksek miktarda etkinlik göz önüne alındığında özellikle ilginçtir.

TL; DR, saniyede 50'den fazla etkinlik içeren bir RTS yapıyor, istemcileri nasıl senkronize ederim?


Belki Eve-online'ın yaptıklarını uygulayabilir ve her şeyin düzgün bir şekilde işlemesine izin vermek için zamanı "yavaşlatır".
Ryan Erb

3
İşte Planetary Annihilation'ın istemci / sunucu modeline zorunlu bir bağlantı: forrestthewoods.ghost.io/… Bu, onlar için çok iyi çalışıyor gibi görünen kilit modeline bir alternatif.
DallonF

Her karo için güncellemeler yerine alınan tüm karolar için tek bir güncelleme göndererek veya Ilmari tarafından yanıtlandığı gibi, oyuncu olmayan eylemleri dağıtarak etkinlik sayısını azaltmayı düşünün.
Lilienthal

Yanıtlar:


12

Saniyede 50 olayı gerçek zamanlı olarak senkronize etme amacınız bana gerçekçi değil gibi geliyor. Bu yüzden 1500 okçu makalesinde kilit adım yaklaşımı hakkında konuşuldu!

Tek bir cümlede: Çok yavaş bir ağ üzerinden çok kısa sürede çok fazla öğeyi senkronize etmenin tek yolu, çok yavaş bir ağ üzerinden çok kısa sürede çok fazla öğeyi Senkronize ETMEMEK, bunun yerine ilerleme durumunu tüm istemcilerde belirleyici olarak yalnızca senkronize etmektir. çıplak ihtiyaçlar (kullanıcı girişi).


6

oyuncunun yaptığı her eylem belirleyicidir, ancak planlanan aralıklarla gerçekleşen olaylar vardır

Bence senin sorunun var; oyununuzda yalnızca bir zaman çizelgesi olmalıdır (oyunu etkileyen şeyler için). Belirli şeylerin saniyede X oranında büyüdüğünü söylüyorsun ; saniyede kaç oyun adımı olduğunu öğrenin ve bunu her Y oyun adımı için X oranına dönüştürün . Daha sonra oyun yavaşlasa da her şey deterministik kalır.

Oyunun bağımsız olarak gerçek zamanlı olarak çalıştırılmasının başka avantajları vardır:

  • mümkün olduğunca hızlı çalıştırarak karşılaştırma yapabilirsiniz
  • geçici etkinlikleri görmek için oyunu yavaşlatarak ve belirtildiği gibi hata ayıklayabilirsiniz
  • oyun deterministik kalır ve bu çok oyunculu için çok önemlidir.

Ayrıca, 50'den fazla etkinlik olduğunda veya saniyeye kadar gecikmeler olduğunda sorunla karşılaştığınızdan söz ettiniz. Bu, 1500 okçuda anlatılan senaryodan çok daha küçüktür , bu nedenle oyununuzu profilleştirip yavaşlayabileceğinizi öğrenin.


1
+1: Çerçeve tabanlı, zaman bazlı değil, doğru seçimdir. Elbette saniyede N kare tutmaya çalışabilirsiniz. Hafif bir aksama, dolu bir desync'ten daha iyidir.
PatrickB

@PatrickB: Birçok oyunun video karelerine bağlı olmayan "simüle edilmiş" bir zaman kullandığını görüyorum . World of Warcraft, her 100ms'de bir mana gibi şeyleri günceller ve Cüce Kalesi, video karesi başına varsayılan olarak 10 kenara ayarlanır.
Mooing Ördek

@Mooing Duck: Yorumum RTS'lere özeldi. Küçük hataların daha sonra tolere edilebileceği ve düzeltilebileceği bir şey için (örneğin MMORPG'ler, FPS'ler), sürekli değerlerin kullanılması sadece iyi değil, aynı zamanda kritiktir. Ancak, birden fazla makinede senkronize edilmesi gereken deterministik simülasyonlar? Çerçevelere yapışır.
PatrickB

4

İlk olarak, sorunu zamanlanmış olaylarla çözmek için , olayları gerçekleştiklerinde değil, başlangıçta zamanlandıklarında yayınlayın . Yani, her saniyede bir "döşemenin enerjisini artırın ( x , y )" mesajı göndermek yerine, yalnızca döşemenin enerjisini artırın ( x , y) ) dolana kadar saniyede bir kez veya" e kadar kesintiye". Her müşteri daha sonra güncellemeleri yerel olarak planlamaktan sorumludur.

Aslında, bu prensibi daha ileriye götürebilirsiniz ve sadece oyuncu eylemlerini iletebilirsiniz : diğer her şey her istemci (ve gerekirse sunucu) tarafından yerel olarak hesaplanabilir.

(Elbette, yanlışlıkla herhangi bir zaman uyumsuzluğu tespit etmek için oyun durumunun sağlama toplamlarını zaman zaman iletmeniz ve bu durumda istemcileri yeniden senkronize etmek için, örneğin sunucunun yetkili kopyasından istemcilere tüm oyun verilerini yeniden göndermek için bir mekanizmaya sahip olmanız gerekir. Ancak bu, yalnızca test sırasında veya nadir arızalarda karşılaşılan nadir bir olay olmalıdır.)


İkincisi, istemcileri senkronize tutmak için oyununuzun deterministik olduğundan emin olun. Diğer cevaplar bunun için zaten iyi tavsiyeler verdi, ancak ne yapacağımın kısa bir özetini vereyim:

  • Her turda veya "kene" 1/50 saniye alarak oyununuzu dahili olarak sıra tabanlı yapın. (Aslında, 1/10 saniye veya daha uzun turlarla uzaklaşabilirsiniz.) Tek bir tur sırasında meydana gelen oyuncu hareketleri eş zamanlı olarak ele alınmalıdır. En azından sunucudan istemcilere kadar olan tüm iletilerin dönüş numarası ile etiketlenmesi gerekir, böylece her istemci her etkinliğin hangi etkinliğin gerçekleşeceğini bilir.

    Oyununuz bir istemci-sunucu mimarisi kullandığından, sunucunun her turda olan bitenin nihai hakemi olarak hareket etmesini sağlayabilirsiniz, bu da bazı şeyleri basitleştirir. Bununla birlikte, istemcilerin kendi eylemlerini sunucudan da yeniden onaylamaları gerektiği anlamına gelir : bir istemci "X birimini bir kutu sola taşırım" mesajını gönderirse ve sunucunun yanıtı X birimi hareketiyle ilgili hiçbir şey söylemezse, istemci bunun gerçekleşmediğini varsayalım ve muhtemelen oynamaya başlamış olabilecek herhangi bir tahmini hareket animasyonunu iptal etmelidir.

  • Aynı istemde gerçekleşen "eşzamanlı" olaylar için tutarlı bir sıra tanımlayın, böylece her istemci bunları aynı sırayla yürütür. Bu sipariş, tüm istemciler (ve sunucu) için belirleyici ve aynı olduğu sürece herhangi bir şey olabilir.

    Örneğin, önce tüm kaynakları arttırabilirsiniz (bir kerede bir döşemedeki kaynak büyümesi diğerindekini engelleyemezse), daha sonra her bir oyuncunun birimlerini önceden belirlenmiş bir sırayla taşıyabilir, ardından NPC birimlerini taşıyabilirsiniz. Oyuncular için adil olmak gerekirse, sıralar arasındaki birim hareket sırasını değiştirmek isteyebilirsiniz, böylece her oyuncu ilk önce eşit sıklıkta ilerler; deterministik olarak yapıldığı sürece bu iyidir (örn. dönüş numarasına göre).

  • Kayan noktalı matematik kullanıyorsanız, bunu katı IEEE modunda kullandığınızdan emin olun. Bu, işleri biraz yavaşlatabilir, ancak bu, müşteriler arasındaki tutarlılığı ödemek için küçük bir fiyattır. Ayrıca iletişim sırasında kazara yuvarlama olmadığından emin olun (örn. Sunucuya yuvarlanmış bir değer ileten, ancak yine de çevrelenmemiş değeri dahili olarak kullanan bir istemci). Yukarıda belirtildiği gibi, senkronizasyondan saptamak ve ondan kurtulmak için bir protokole sahip olmak, her ihtimale karşı iyi bir fikirdir.


1
Ayrıca, başlatmak için RNG'yi senkronize edin ve sadece sunucu size söylediğinde senkronize edilen RNG'den çekin. Starcraft1, uzun bir süre boyunca RNG tohumunun tekrarlar sırasında kaydedilmediği bir hataya sahipti, bu yüzden tekrarlar gerçek oyunlardan yavaşça sapacaktı.
Mooing Ördek

1
@MooingDuck: İyi bir nokta. Aslında, mevcut RNG tohumunun her dönüşte iletilmesini öneririm, böylece RNG senkronizasyonunun hemen algılanması sağlanır. Ayrıca, UI kodunuzun herhangi bir rasgele duruma ihtiyacı varsa, bunu oyun mantığı için kullanılanla aynı RNG örneğinden çekmeyin.
Ilmari Karonen

3

Oyun mantığınızı gerçek zamanlıdan tamamen bağımsız hale getirmeli ve aslında Sıra Tabanlı yapmalısınız. Bu şekilde, "fayans enerji değişiminin gerçekleştiği" dönüşünü tam olarak bilirsiniz. Sizin durumunuzda, her dönüş saniyenin 1 / 50'sidir.

Bu şekilde yalnızca oyuncu girdileri hakkında endişelenmeniz gerekir, diğer her şey oyun mantığı tarafından yönetilir ve tüm istemcilerde tamamen aynıdır. Net gecikme veya ekstra karmaşık hesaplama nedeniyle oyun bir an dursa bile, olaylar herkes için senkronize oluyor.


1

Her şeyden önce, hesaplama için IEEE-754'ü sıkı bir şekilde kullanmayı belirtmediğiniz sürece (yavaş olacaktır) PC float / double math'in deterministik olmadığını anlamalısınız.

O zaman ben bunu nasıl uygulayacağım: istemci sunucuya bağlanmak ve zaman sincronize (ping gecikme dikkat!) (Uzun oyun için zaman damgası / dönüş yeniden senkronize etmek gerekebilir)

şimdi, bir istemci her eylem yaptığında, bir zaman damgası / dönüş içerir ve kötü zaman damgası / dönüşünü reddetmek sunucuya bağlıdır. Ardından sunucu eylemi istemcilere geri gönderir ve her dönüşte "kapalı" (aka sunucu bu kadar eski dönüş / zaman damgası kabul etmez), sunucu istemcilere gönderme ve sonlandırma eylemi.

Müşteriler 2 "dünyaya" sahip olacak: biri son dönüş ile senkronize, diğeri son dönüşten başlayarak, mevcut müşteri dönüş / zaman damgasına kadar kuyruğa gelen eylemi toplayarak hesaplanıyor.

sunucu biraz eski eylemi kabul edeceğinden, istemci kendi eylemini doğrudan kuyruğa ekleyebilir, böylece en azından kendi eyleminiz için ağ üzerinden gidiş dönüş süresi gizlenir.

son şey daha fazla eylem kuyruğudur, böylece MTU paketini doldurabilir ve daha az protokol yüküne neden olabilirsiniz; güzel bir fikir bunu sunucuda yapmaktır, bu nedenle her son dönüş etkinliği kuyrukta eylem içerir.

Bu algoritmayı gerçek zamanlı çekim oyununda kullanıyorum ve iyi çalışıyor (istemci kendi eylemini chaching ile ve istemci olmadan, 20 / 50ms kadar düşük ping ile), ayrıca her X son dönüş sunucusu özel bir "tüm müşteri haritası "paket, sürüklenen değerleri düzeltmek için.


Kayan nokta matematik sorunları genellikle bu şekilde önlenebilir - bir RTS'de, simülasyonu ve hareketi genellikle tamsayı / sabit nokta ile kolayca yapabilir ve kayan noktayı yalnızca oyun davranışını etkilemeyen ekran katmanı için kullanabilirsiniz.
Peteris

Tamsayı ile sekizgen bir tahta olmadığı sürece yatay fayans yapmak zordur. Sabit nokta için hw hızlanma yoktur, bu yüzden floattan daha yavaş olabilir ieee754
Lesto 28:14
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.