Wie synchronisiere ich den Status eines Multiplayer-Spiels effizienter als Aktualisierungen des vollständigen Status?


10

Ich habe schon ein wenig Spielnetzwerkcodierung durchgeführt, aber hauptsächlich mit TCP für Spiele ohne Echtzeitbedarf. Ich arbeite an einem 2D-Java-Spiel mit vernetztem Multiplayer. Zum Lernen möchte ich dies selbst tun, ohne eine vorhandene Netzwerk-API.

Wie kann ich den Spielstatus, der von einem Server an Clients gesendet wird, effizient darstellen? Es gibt die naheliegendste, aber wahrscheinlich am wenigsten effiziente Möglichkeit, eine Art Spielstatus-Kontextobjekt mit dem Standort, dem Animationsstatus usw. jedes Spielers zu erstellen und dieses bei jedem Update an jeden Spieler zu senden . Das scheint nicht besonders schwierig zu implementieren, wäre aber wahrscheinlich zu groß, um etwas in der Nähe der Echtzeitinteraktion zu erreichen (natürlich sind meine Erfahrungen damit begrenzt, so dass ich möglicherweise falsch liege).

Gibt es eine solide Methode, die einer von Ihnen zuvor verwendet hat, um nur Zustandsänderungen zu übertragen, und gibt es sogar eine ausreichend große Leistungsunterschiede, die die zusätzliche Arbeit wert ist?


2
Probieren Sie jeden Frame aus und wenn es zu langsam ist (für ein etwas einfaches 2D-Spiel ist es wahrscheinlich effizient genug), versuchen Sie es zu optimieren. Wenn es gut funktioniert, funktioniert es gut und Sie müssen es nicht ändern, es sei denn, Sie bemerken später, dass Ihr Netzwerk ein Engpass ist.
Robert Rouhani

Antworten:


10

Das regelmäßige Übertragen des vollständigen Spielstatus ist normalerweise nicht möglich, hängt jedoch stark von der Komplexität Ihres Spiels ab. Für ein einfaches Spiel mit einem kleinen Weltmodell kann es funktionieren.

Ich persönlich habe mit folgendem Modell viel mehr Erfolg gehabt:

  • Spielstatus gespeichert in einem genau definierten Objektmodell in einer räumlichen Datenstruktur (z. B. einem Octree)
  • Alle Änderungen am Spielstatus (ob auf dem Client oder dem Server) werden als Ereignisse bezeichnet. Ein Ereignis kann eine Eigenschaftsänderung an einem Spielobjekt, eine Änderung an einem Kartenplättchen, die Bewegung eines Spielobjekts usw. sein.
  • Die Spiel-Engine auf dem Server erzeugt im Verlauf des Spiels einen Strom von Ereignissen. Diese werden direkt auf den Spielstatus des Servers angewendet.
  • Die Ereignisse werden auch an Spieler gesendet, jedoch nur, wenn das Ereignis für diesen Spieler relevant ist (z. B. ist das Ereignis von der aktuellen Position aus sichtbar?).
  • Änderungen in der Sichtbarkeit des Spielers können auch dazu führen, dass Ereignisse neue Teile der Karte usw. "aufdecken", wenn sich der Spieler bewegt. Dies kann auch verwendet werden, um sicherzustellen, dass der Spieler eine genaue erste Ansicht des relevanten Spielstatus erhält, wenn er zum ersten Mal am Spiel teilnimmt.
  • Der Spielstatus für den Spieler wird mit allen empfangenen Ereignissen aktualisiert. Als solches hat es nur ein Teilmodell des Spielstatus, sollte jedoch mit dem Server synchron bleiben, vorausgesetzt, alle Ereignisse werden korrekt verarbeitet

Dies hat mir auch bei recht großen Spielwelten eine gute Leistung gebracht.

Ein weiterer Tipp: Lassen Sie den Client sich ohne Bezugnahme auf den Server um Animation, Partikeleffekte usw. kümmern. Es macht keinen Sinn, diese zu übertragen - sie müssen nur durch die entsprechenden Spielereignisse "ausgelöst" werden.


6

Die Synchronisation ist normalerweise in zwei Teile unterteilt: inkrementell und absolut.

Manchmal müssen Sie alles übertragen, es ist groß, aber wenn Sie es richtig verpacken, können Sie dies alle paar Sekunden tun. Es ist gut, alles einzurichten und die Fehler inkrementeller Aktualisierungen zu korrigieren.

Um eine Echtzeiterfahrung zu erzielen, müssen Sie einige Änderungen schnell übertragen, aber nur die Attribute, die sich ändern können. Wenn beispielsweise eine Rakete in einer geraden Linie fliegt, müssen Sie die Position nicht aktualisieren. Jeder Kunde kann sie vom Startpunkt aus berechnen. Wenn es jedoch trifft, können Sie eine Nachricht darüber generieren, sodass jeder Client die Rakete an der richtigen Stelle explodieren lassen kann. Kleinere Störungen können ignoriert werden.

Natürlich aktualisieren Sie Sachen nur, wenn sie den Client beeinflussen können! Etwas weit weg vom Bildschirm ist es nicht wert. Einige Werte können weniger häufig aktualisiert werden. Zum Beispiel sind die Positionen wichtig, um mehr oder weniger genau zu sein. Ereignisse (Tod, Schuss, Explosion usw.) müssen sofort gesendet werden, während nicht direkt wichtige Werte niedrigere Aktualisierungsperioden haben können, z. B. Anzeigetafel, Chat.

Das Packen von Daten ist ebenfalls wichtig. Sie können ungefähr 1400 Bytes (konfigurationsabhängig, dies ist die Standardeinstellung) in einem UDP-Paket übertragen. In der Regel sind einige Bytes Header vorhanden. So können Sie problemlos 50-100 Einheitenpositionen in einem Paket aktualisieren.


Danke für den Rat Matzi. Ich arbeite immer noch an der Implementierung des Servers und des Clients, aber ich werde in ein paar Tagen noch einmal nachsehen und wahrscheinlich Ihre Antwort akzeptieren.
Haz

Viel Glück! ;)
Matzi

1

Abhängig von Ihrem Spiel können Sie ein "synchronisiertes Ausführungsmodell" in Betracht ziehen, bei dem jeder Client zufällig dasselbe Spiel spielt, indem er einfach nicht deterministische Eingaben wie Tastatur- / Joystick-Eingaben und Timer-Ereignisse teilt. (Im Vergleich zu einem Modell, bei dem jeder Client lokale Simulationen ausführt und erwartet, dass Ergebnisse aus Remote-Simulationen integriert werden). Ihre Spiel-Engine muss im Allgemeinen vollständig deterministisch sein, damit dies funktioniert. Dies kann je nach Spiel eine schwere Belastung sein. Wenn das Spiel jedoch bereits deterministisch ist, kann dies ein einfacherer Ansatz sein.

Dieser # AltDevBlogADay-Beitrag behandelt einige Aspekte dieses Ansatzes in einem modernen RTS (insbesondere, wie Sie erkennen, wann Ihre Kunden "verschiedene" Spiele ausführen ).

Denken Sie jedoch daran, es einfach zu halten, bis das Gegenteil bewiesen ist. :) :)


1
Dies sind gute Lesungen des Entwicklers von Factorio, der diesen Ansatz verwendet, und weisen auf die Komplexität dieses Ansatzes hin, zeigen aber auch, dass er realisierbar ist: factorio.com/blog/post/fff-76 factorio.com/blog/post/fff -147 factorio.com/blog/post/fff-188
AaronLS
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.