Wie oft muss ein Game Client über die Welt aktualisiert werden?


10

Mit socket.io habe ich eine ähnliche Kommunikation wie bei anderen MMORPGs, eine ständige Verbindung mit Nachrichten.

In meinem bisherigen Design sendet der Client die Position und den Animationsrahmen des Players mit jedem Aktualisierungsrahmen. Wenn der Server diese Nachricht erhält, sendet er sie an alle Clients, die dann die Grafik entsprechend verschieben.

Wäre es eine bessere Idee, diese zu "sammeln" und sie beispielsweise einmal in 1/10 Sekunde zu senden?

Sollte der Kunde auch viele verschiedene Nachrichten senden (Exp gewonnen, auf Artikel geklickt), sobald sie auftreten, oder vielmehr nur eine gesammelte? Der erste würde einfacher implementiert werden.

Antworten:


16

Ich werde dies von einer hochrangigen Diskussion aus angehen und dann auf Ihre Fragen hinarbeiten. Aus Gründen der Offenlegung habe ich keine persönlichen Erfahrungen mit socket.io, aber viel Erfahrung mit dem Problembereich in Bezug auf MMORPGs.

Das Design der Netzwerkarchitektur einer MMORPGs-Engine und / oder die Auswahl eines Middleware- oder Open Source-Projekts zur Bereitstellung der Funktionalität ist eine der schwierigeren Entscheidungen, die vom Spieldesign, dem Budget und dem technischen Know-how des Teams beeinflusst werden. Die endgültige Wahl beeinflusst andere Architekturentscheidungen (und manchmal auch Entwurfsentscheidungen).

Als Entwickler von MMORPGs planen wir einen großen Erfolg (oft auch als katastrophaler Erfolg bezeichnet), bei dem eine große Anzahl Warnlichter und Sirenen auslöst. Eine der beängstigend großen Zahlen, die auftaucht, sind Algorithmen, die N-Quadrat sind (N ^ 2 im Folgenden). In Ihrer Frage ist mir als erstes aufgefallen, dass es sich nach dem Entwurf anhörte, an den eine Entität Informationen senden sollte alle anderen verbundenen Einheiten. Dies ist das klassische Beispiel für ein N ^ 2-Problem.

MMOs nähern sich im Allgemeinen der Lösung von N ^ 2-Problemen, indem sie das Problem auf verschiedene Weise angreifen. Bewusstseins-Systeme (situativ, räumlich usw.), bei denen eine Entität eine Teilmenge aller anderen Entitäten kennt, die Spieler in verschiedene "Shards" aufteilen, Spieler in "Zonen" aufteilen und / oder instanziieren und Spielmechaniken implementieren, die zu viele entmutigen Spieler aus der Versammlung (Asheron Call's Teleport-Stürme) usw.

Die meisten MMORPGs und viele FPS-Engines verfügen über ziemlich ausgefeilte Netzwerkarchitekturen, die eine Vielzahl von Funktionen unterstützen, darunter:

  • zuverlässige und unzuverlässige Kommunikationswege (TCP, benutzerdefinierte Implementierungen zuverlässiger UDP- und UDP-Pakete)
  • Bandbreitenformung (Priorisierung, Lebensdauer usw.)
  • automatische Replikation von Feld- / Viarable-Daten und Funktionsaufrufen
  • atomare Datensätze (dh Daten, die zusammen kommuniziert werden)
  • diskrete Aktualisierungen (dh wo jeder Übergang wichtig ist)
  • Latenzkorrektur
  • Eine Vielzahl von Tricks, mit denen sich der Kunde ansprechbar fühlt

Ich finde, dass die Unreal Networking Documentation und die Valve Networking Documentation eine gute Einführung in eine Vielzahl von Themen bieten.

Kommen wir nun zu den Fragen.

Wäre es eine bessere Idee, diese zu "sammeln" und sie beispielsweise einmal in 1/10 Sekunde zu senden?

Es ist schwierig, hier eine einfache Ja- oder Nein-Antwort zu geben ... da dies von der Skala (Anzahl der beobachtenden Entitäten), der Häufigkeit der Aktualisierungen und der Größe der Aktualisierungen abhängt. Zum Beispiel könnte es schrecklich falsch sein, sie alle zu sammeln, wenn die Größe der Updates irgendwo einen Puffer sprengen könnte.

Der Client für MMORPGs und FPS-Spiele ist im Allgemeinen so konzipiert, dass er etwas visualisiert, das "richtig" aussieht, auch wenn er kein Update für viel mehr Update-Frames als "normal" erhält. Wenn Sie unzuverlässige Kommunikation (UDP) verwenden, können Sie davon ausgehen, dass einige Aktualisierungen ins Leere gehen. Clients können dies durch häufigeres Aktualisieren ausgleichen, als dies bei einem zuverlässigen Transport der Fall ist.

Aus einer flüchtigen Überprüfung der Dokumentation zu socket.io geht hervor, dass sie sowohl zuverlässige als auch unzuverlässige (in ihrer Terminologie flüchtige) Kommunikationswege unterstützt.

Ich würde dies zuerst angehen, indem ich mich mit der Frage befasse, in welcher Häufigkeit Aktualisierungen erforderlich sind ...

Wenn sich ein Spieler mit einer konstanten Geschwindigkeit in einer geraden Linie bewegt, ist eine niedrigere Aktualisierungsfrequenz in Ordnung, da die beobachtenden Clients mit hoher Genauigkeit vorhersagen können, wo sich der Spieler zu jedem Zeitpunkt befindet. Wenn sich ein Spieler in einem engen Kreis dreht oder schnelle Richtungsänderungen vornimmt, sind viel häufigere Aktualisierungen erforderlich. Umgekehrt gibt es keinen Grund, Bewegungsaktualisierungen zu versenden, wenn sich ein Spieler überhaupt nicht bewegt.

Egal was passiert, ist wahrscheinlich (im Allgemeinen) nicht erforderlich, um Updates für jeden Frame vom Client an den Server zu senden. Der Server selbst kann wählen, ob er Nachrichten an jeden Frame senden oder verzögern möchte (siehe Bandbreitenformung, Priorisierung und Aktualisierungslebensdauer).

Andere Arten von Updates haben andere Eigenschaften ... Betrachten Sie beispielsweise ein "Gesundheits" -Feld, das geändert wird, wenn ein Spieler oder eine Kreatur beschädigt wird. Eine Möglichkeit, dies zu implementieren, besteht darin, jede Änderung sofort zu übertragen, wenn dies geschieht. Dies führt jedoch zu einer Verschwendung von Verarbeitung und Bandbreite, wenn der Wert in einem Frame oder in aufeinanderfolgenden Frames mehrmals geändert wird (Netzwerkarchitekturen, die die Bandbreitenformung implementieren, lösen dieses Problem, indem die Aktualisierungen zusammengeführt werden Nur der aktuellste wird an einen beobachtenden Client gesendet, wenn dieser über verfügbare Bandbreite verfügt.

Sollte der Kunde viele verschiedene Nachrichten senden (Exp gewonnen, auf Artikel geklickt), sobald sie auftreten, oder eher nur eine gesammelte?

Auch hier wird keine einfache Ja- oder Nein-Antwort funktionieren. Je nachdem, was genau Sie unter gesammelt verstehen, können beide unter verschiedenen Umständen richtig sein und auch von der Implementierung der Netzwerkschicht abhängen.

Das Sammeln von Nachrichten für eine bestimmte Entität, die als eine Nachricht gesendet werden soll, kann (abhängig von der Implementierung) den Bandbreitenaufwand für das Senden einer Nachricht verringern (wodurch Ihre Kosten gesenkt werden) Anforderungen im Vergleich zu einem einfacheren spezifischen Nachrichtentyp.

Wenn ich die Dokumentation zu socket.io überprüfe, scheint es mir, dass der Nachrichtenaufwand am oberen Ende des Spektrums liegt, was das Sammeln von Aktualisierungen zum Senden als aggregierte Nachricht im Gegensatz zu vielen einzelnen Aktualisierungen begünstigen würde.

Ich würde empfehlen, alle Updates zu überprüfen, die Sie replizieren möchten. Beispielsweise senden die meisten MMORPGs und FPSs nicht die Mühe, an X-Ereignisse angeklickte Spieler an beobachtende Clients zu senden, es sei denn, dies würde zu einer Statusänderung für ein Objekt führen, von dem sie ebenfalls Kenntnis hatten .


3
+1 Tolle Antwort. Ich würde zusätzlich vorschlagen, einfach zu beginnen und von dort aus zu optimieren. Sicher, Spam-Nachrichten werden wild und sehen, wie Ihre Bandbreite anfängt zu sprudeln. Deshalb ist es gut, Stresstests durchgeführt zu haben. Sie implementieren den naivsten Ansatz und führen einige Tests durch. Sie machen einige Optimierungen; Sie erneut Stresstest, Sie optimieren weiter, spülen, wiederholen. Wenn es absolut trivial ist, am ersten Tag eine Optimierung durchzuführen, fahren Sie fort. Beachten Sie jedoch, dass selbst die trivialsten Optimierungen Annahmen enthalten können, die später in einer Produktionsumgebung widerlegt werden können.
Ingenieur

5

Hier ist ein Beispiel aus der Praxis : RuneScape "tickt" einmal alle ~ 0,6 Sekunden. Sie können darüber lesen Sie hier . Ich stelle mir vor, dass dies die Dinge aus ihrer Sicht vereinfacht, da sie in ihren Skripten wahrscheinlich Timings und Verzögerungen in Ticks anstelle von Millisekunden angeben. Und natürlich sind Benutzer mit langsamen Verbindungen bei einer so großen / langsamen Tick-Rate im Vergleich zu anderen Spielern nicht im Nachteil.


0

Eine Arbeitsweise besteht darin, die tatsächlichen Datenänderungen von der Übertragung dieser Änderungen zu entkoppeln. Wenn sich ein Objekt ändert, nehmen Sie die Änderung vor und setzen Sie ein "Dirty" -Flag. Wenn Änderungen übertragen werden sollen, senden Sie nur Daten für markierte Objekte und löschen Sie das Flag. Werte, die mehrmals geändert werden, werden hier automatisch zusammengeführt, da Ihr Update nur den Status zum Zeitpunkt der Übertragung sendet. Sie können auch Unterobjekte oder einzelne Eigenschaften kennzeichnen, sodass Sie nur das übertragen, was sich geändert hat.

Oh, und normalerweise würden Sie die Animationsframes nicht an den Server oder die anderen Clients senden. Der genaue Animationsstatus wird normalerweise als Präsentationsdetail betrachtet und jedem Client zur Lösung überlassen. Stattdessen müssen Sie nur sicherstellen, dass jeder Client weiß, welche Animation gerade abgespielt wird.


Ich würde nicht? Was ist mit Posen oder so, ich muss sicher sein, dass alle Kunden das gleiche sehen?
Lanbo

Es ist ziemlich unpraktisch sicherzustellen, dass jeder Client genau dasselbe sieht, da das Reisen von Informationen einige Zeit in Anspruch nimmt, unterschiedliche Clients unterschiedliche Netzwerklatenzzeiten aufweisen, mit unterschiedlichen Geschwindigkeiten rendern usw. Der Versuch, alle dazu zu bringen, gleichzeitig genau denselben Frame anzuzeigen, ist normalerweise ein Problem vergebliches Unterfangen. Stattdessen können Sie einfach sicherstellen, dass sie ungefähr zur gleichen Zeit dieselbe Animation starten, und das ist gut genug.
Kylotan
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.