Ich arbeite an einem isometrischen 2D-Spiel mit moderatem Multiplayer-Modus, bei dem ungefähr 20 bis 30 Spieler gleichzeitig mit einem permanenten Server verbunden sind. Ich hatte einige Schwierigkeiten, eine gute Implementierung für die Bewegungsvorhersage zu finden.
Physik / Bewegung
Das Spiel hat keine echte physische Implementierung, sondern verwendet die Grundprinzipien, um Bewegung zu implementieren. Anstatt fortlaufend Eingaben abzufragen, werden Statusänderungen (z. B. / Maus runter / rauf / Verschieben) verwendet, um den Status der Charakterentität zu ändern, die der Spieler kontrolliert. Die Richtung des Spielers (dh / Nordosten) wird mit einer konstanten Geschwindigkeit kombiniert und in einen wahren 3D-Vektor umgewandelt - die Geschwindigkeit der Entität.
In der Hauptspielschleife wird "Update" vor "Draw" aufgerufen. Die Aktualisierungslogik löst eine "Physikaktualisierungsaufgabe" aus, die alle Entitäten mit einer Geschwindigkeit ungleich Null verfolgt, wobei eine sehr grundlegende Integration zum Ändern der Entitätenposition verwendet wird. Beispiel: entity.Position + = entity.Velocity.Scale (ElapsedTime.Seconds) (wobei "Seconds" ein Gleitkommawert ist, der gleiche Ansatz jedoch für Ganzzahlwerte in Millisekunden funktioniert).
Der entscheidende Punkt ist, dass keine Interpolation für die Bewegung verwendet wird - die rudimentäre Physik-Engine kennt kein Konzept eines "vorherigen Zustands" oder "aktuellen Zustands", sondern nur eine Position und eine Geschwindigkeit.
Statusänderungs- und Aktualisierungspakete
Wenn sich die Geschwindigkeit des Charakters, den der Spieler kontrolliert, ändert, wird ein "Avatar bewegen" -Paket an den Server gesendet, das den Aktionstyp (Stehen, Gehen, Laufen), die Richtung (Nordosten) und die aktuelle Position des Charakters enthält. Dies unterscheidet sich von der Funktionsweise von 3D-Ego-Spielen. In einem 3D-Spiel kann sich die Geschwindigkeit (Richtung) von Bild zu Bild ändern, wenn sich der Spieler bewegt. Das Senden jeder Zustandsänderung würde effektiv ein Paket pro Rahmen übertragen, was zu teuer wäre. Stattdessen scheinen 3D-Spiele Statusänderungen zu ignorieren und "Statusaktualisierungs" -Pakete in einem festen Intervall zu senden - etwa alle 80-150 ms.
Da Geschwindigkeits- und Richtungsaktualisierungen in meinem Spiel viel seltener vorkommen, kann ich jede Statusänderung nicht mehr senden. Obwohl alle physikalischen Simulationen mit der gleichen Geschwindigkeit und deterministisch ablaufen, ist die Latenz nach wie vor ein Problem. Aus diesem Grund sende ich routinemäßig Positionsaktualisierungspakete aus (ähnlich wie bei einem 3D-Spiel), allerdings viel seltener - derzeit alle 250 ms, aber ich vermute, mit einer guten Vorhersage kann ich es leicht in Richtung 500 ms steigern. Das größte Problem ist, dass ich jetzt von der Norm abgewichen bin - alle anderen Online-Dokumentationen, Anleitungen und Beispiele senden Routine-Updates und interpolieren zwischen den beiden Zuständen. Es scheint mit meiner Architektur nicht kompatibel zu sein, und ich muss einen besseren Algorithmus für die Bewegungsvorhersage entwickeln, der einer (sehr einfachen) "vernetzten Physik" -Architektur näher kommt.
Der Server empfängt dann das Paket und bestimmt die Geschwindigkeit des Spielers anhand seines Bewegungstyps anhand eines Skripts (Kann der Spieler laufen? Ermittelt die Laufgeschwindigkeit des Spielers). Sobald es die Geschwindigkeit hat, kombiniert es sie mit der Richtung, um einen Vektor zu erhalten - die Geschwindigkeit des Objekts. Einige Cheat-Erkennungen und grundlegende Überprüfungen werden durchgeführt, und die Entität auf der Serverseite wird mit der aktuellen Geschwindigkeit, Richtung und Position aktualisiert. Grundlegende Drosselung wird auch durchgeführt, um zu verhindern, dass Spieler den Server mit Bewegungsanforderungen überfluten.
Nach dem Aktualisieren seiner eigenen Entität sendet der Server ein "Avatar-Positionsaktualisierungs" -Paket an alle anderen Spieler in Reichweite. Das Positionsaktualisierungspaket wird verwendet, um die clientseitigen Physiksimulationen (Weltzustand) der entfernten Clients zu aktualisieren und eine Vorhersage und eine Verzögerungskompensation durchzuführen.
Vorhersage und Lag Compensation
Wie oben erwähnt, sind Kunden für ihre eigene Position maßgeblich. Außer im Falle von Betrug oder Anomalien wird der Avatar des Kunden niemals vom Server neu positioniert. Für den Avatar des Kunden ist keine Extrapolation ("Jetzt verschieben und später korrigieren") erforderlich - was der Spieler sieht, ist korrekt. Es ist jedoch eine Extrapolation oder Interpolation für alle entfernten Entitäten erforderlich, die sich bewegen. In der lokalen Simulations- / Physik-Engine des Kunden ist eindeutig eine Art Vorhersage und / oder Verzögerungskompensation erforderlich.
Probleme
Ich habe mit verschiedenen Algorithmen zu kämpfen und habe eine Reihe von Fragen und Problemen:
Soll ich extrapolieren, interpolieren oder beides? Mein "Bauchgefühl" ist, dass ich eine reine Extrapolation basierend auf der Geschwindigkeit verwenden sollte. Die Zustandsänderung wird vom Client empfangen, der Client berechnet eine "vorhergesagte" Geschwindigkeit, die die Verzögerung ausgleicht, und das reguläre Physiksystem erledigt den Rest. Es fühlt sich jedoch im Widerspruch zu allen anderen Beispielcodes und Artikeln an - sie scheinen alle eine Reihe von Zuständen zu speichern und eine Interpolation ohne eine Physik-Engine durchzuführen.
Wenn ein Paket eintrifft, habe ich versucht, die Position des Pakets mit der Geschwindigkeit des Pakets über einen festgelegten Zeitraum (z. B. 200 ms) zu interpolieren. Ich nehme dann die Differenz zwischen der interpolierten Position und der aktuellen "Fehler" -Position, um einen neuen Vektor zu berechnen und diesen anstelle der gesendeten Geschwindigkeit auf dem Objekt zu platzieren. Die Annahme ist jedoch, dass in diesem Zeitintervall ein anderes Paket eintrifft, und es ist unglaublich schwierig zu "erraten", wann das nächste Paket eintrifft - zumal nicht alle in festen Intervallen eintreffen (dh auch / state-Änderungen). Ist das Konzept grundlegend fehlerhaft oder ist es korrekt, erfordert jedoch einige Korrekturen / Anpassungen?
Was passiert, wenn ein Remote-Player stoppt? Ich kann die Entität sofort stoppen, aber sie wird an der "falschen" Stelle positioniert, bis sie sich wieder bewegt. Wenn ich einen Vektor abschätze oder zu interpolieren versuche, habe ich ein Problem, weil ich den vorherigen Status nicht speichere - die Physik-Engine kann nicht sagen, dass Sie anhalten müssen, nachdem Sie Position X erreicht haben. Es versteht einfach eine Geschwindigkeit, nichts Komplexeres. Ich zögere es, der Entities- oder Physics-Engine die "Packet Movement State" -Informationen hinzuzufügen, da dies gegen grundlegende Designprinzipien verstößt und den Netzwerkcode über den Rest der Game-Engine verteilt.
Was soll passieren, wenn Entitäten kollidieren? Es gibt drei Szenarien: Der steuernde Spieler kollidiert lokal, zwei Entitäten kollidieren auf dem Server während einer Positionsaktualisierung oder eine Remote-Entitätsaktualisierung kollidiert auf dem lokalen Client. In allen Fällen bin ich mir nicht sicher, wie ich mit der Kollision umgehen soll - abgesehen vom Schummeln sind beide Zustände "korrekt", jedoch zu unterschiedlichen Zeitpunkten. Im Fall einer entfernten Entität ist es nicht sinnvoll, sie durch eine Wand zu ziehen. Daher führe ich auf dem lokalen Client eine Kollisionserkennung durch und veranlasse, dass sie "stoppt". Basierend auf dem obigen Punkt 2 könnte ich einen "korrigierten Vektor" berechnen, der kontinuierlich versucht, das Objekt "durch die Wand" zu bewegen, was niemals gelingt - der entfernte Avatar bleibt dort hängen, bis der Fehler zu hoch wird und "einrastet" Position. Wie funktionieren Spiele um dieses Problem herum?
quelle
Antworten:
Das einzige, was zu sagen ist, ist, dass 2D, Isometrie, 3D, sie alle gleich sind, wenn es um dieses Problem geht. Da Sie viele Beispiele für 3D sehen und nur ein mit 2D-Oktanten begrenztes Eingabesystem mit augenblicklicher Geschwindigkeit verwenden, bedeutet dies nicht, dass Sie Netzwerkprinzipien verwerfen können, die sich in den letzten 20 Jahren entwickelt haben.
Designprinzipien müssen verdammt sein, wenn das Gameplay gefährdet ist!
Wenn Sie frühere und aktuelle Informationen verwerfen, werden die wenigen Informationen verworfen, die Ihr Problem lösen könnten. Zu diesen Daten würde ich Zeitstempel und berechnete Verzögerungen hinzufügen, damit die Extrapolation besser vorhersagen kann, wo sich dieser Spieler befindet, und die Interpolation Geschwindigkeitsänderungen mit der Zeit besser ausgleichen kann.
Dies ist ein wichtiger Grund, warum Server offenbar viele Statusinformationen senden und keine Eingaben steuern. Ein weiterer wichtiger Grund hängt davon ab, welches Protokoll Sie verwenden. UDP mit akzeptiertem Paketverlust und Auslieferung nicht in Ordnung? TCP mit versicherter Zustellung und Wiederholungsversuchen? Mit jedem Protokoll erhalten Sie Pakete zu seltsamen Zeiten, verzögert oder in einer Flut von Aktivitäten übereinander gestapelt. All diese seltsamen Pakete müssen in einen Kontext passen, damit der Client herausfinden kann, was los ist.
Auch wenn Ihre Eingaben auf 8 Richtungen beschränkt sind, kann die tatsächliche Änderung jederzeit erfolgen - die Durchsetzung eines 250-ms-Zyklus wird schnelle Spieler nur frustrieren. 30 Spieler sind nichts Besonderes für einen Server. Wenn Sie von Tausenden sprechen ... selbst dann sind Gruppen von ihnen auf mehrere Boxen aufgeteilt, sodass einzelne Server nur eine angemessene Last tragen.
Haben Sie jemals ein Profil für eine Physik-Engine wie Havok oder Bullet erstellt? Sie sind wirklich ziemlich optimiert und sehr, sehr schnell. Möglicherweise geraten Sie in die Falle, dass die Operation ABC langsam ist und etwas optimiert, das sie nicht benötigt.
quelle
Ihr Server ist also im Wesentlichen ein "Schiedsrichter"? In diesem Fall glaube ich, dass alles in Ihrem Klienten deterministisch sein muss; Sie müssen sicherstellen, dass auf jedem Client immer das gleiche Ergebnis erzielt wird.
Bei Ihrer ersten Frage sehe ich nicht ein, wie Sie vorhersagen können, in welche Richtung der Spieler das nächste Mal abbiegen wird, wenn der lokale Spieler die Richtung des anderen Spielers erhält, abgesehen davon, dass er seine Bewegung im Laufe der Zeit verzögern und Kollisionen anwenden kann Umgebung mit 8 Richtungen.
Wenn Sie die Aktualisierung der "realen Position" jedes Spielers erhalten (möglicherweise könnten Sie versuchen, auf dem Server zu taumeln), müssen Sie die Position und Richtung des Spielers interpolieren. Wenn die "erratene" Position sehr falsch ist (dh der Spieler hat die Richtung unmittelbar nach dem Senden des letzten Richtungspakets komplett geändert), besteht eine große Lücke. Dies bedeutet, dass entweder der Spieler die Position überspringt oder Sie zur nächsten erratenen Position interpolieren können . Dies sorgt für eine gleichmäßigere Interpolation im Laufe der Zeit.
Wenn Entitäten kollidieren und Sie ein deterministisches System erstellen können, kann jeder Spieler die Kollision lokal simulieren, und die Ergebnisse sollten nicht zu weit von der Realität entfernt sein. Jeder lokale Computer sollte die Kollision für beide Spieler simulieren. In diesem Fall muss sichergestellt werden, dass der endgültige Status nicht blockiert und akzeptabel ist.
Auf der Serverseite kann ein Schiedsrichterserver immer noch einfache Berechnungen durchführen, um beispielsweise die Geschwindigkeit eines Spielers über kurze Zeiträume hinweg zu überprüfen und als einfachen Anti-Cheat-Mechanismus zu verwenden. Wenn Sie die Überwachung jedes Spielers über 1 Sekunde hintereinander durchlaufen, ist Ihre Cheat-Erkennung skalierbar. Das Auffinden von Cheatern dauert jedoch länger.
quelle
Können Sie Geschwindigkeit nicht in Ihre Zustandsänderungsnachrichten aufnehmen und diese verwenden, um Bewegungen vorherzusagen? Angenommen, die Geschwindigkeit ändert sich erst, wenn Sie eine Meldung erhalten, die besagt, dass sie sich geändert hat. Ich denke, Sie senden bereits Positionen. Wenn also etwas "übersteuert", haben Sie beim nächsten Update ohnehin die richtige Position. Während der Aktualisierung können Sie dann die Positionen schrittweise ändern, indem Sie die Geschwindigkeit der letzten Nachricht verwenden und die Position überschreiben, wenn eine Nachricht mit einer neuen Position empfangen wird. Dies bedeutet auch, dass Sie eine Nachricht senden müssen, wenn sich die Position nicht ändert, die Geschwindigkeit jedoch (wenn dies in Ihrem Spiel sogar ein gültiger Fall ist), aber wenn überhaupt, hat dies keine großen Auswirkungen auf Ihre Bandbreitennutzung.
Die Interpolation sollte hier keine Rolle spielen, z. B. wenn Sie wissen, wo sich etwas in Zukunft befindet, ob Sie es haben, welche Methode Sie verwenden usw. Sind Sie vielleicht mit der Extrapolation verwechselt? (wofür ich einen einfachen Ansatz beschreibe)
quelle
Meine ersten Fragen wären: Was ist falsch daran, ein Modell zu verwenden, bei dem der Server die Berechtigung hat? Warum ist es wichtig, ob die Umgebung 2D oder 3D ist? Es würde Ihren Cheat-Schutz viel einfacher machen, wenn Ihr Server autorisierend wäre.
Bei der Vorhersage müssen mehrere Zustände (oder zumindest Deltas) auf dem Client verwaltet werden, damit der vom Server empfangene autorisierende Zustand / Delta mit dem des Clients verglichen und die erforderlichen vorgenommen werden können Korrekturen. Die Idee ist, so viel wie möglich deterministisch zu halten, um den Umfang der erforderlichen Korrekturen zu minimieren. Wenn Sie die vorherigen Zustände nicht beibehalten, können Sie nicht wissen, ob auf dem Server etwas anderes passiert ist.
Warum müssen Sie interpolieren? Der autorisierende Server sollte alle fehlerhaften Bewegungen außer Kraft setzen.
Dies sind Situationen, in denen ein Konflikt zwischen dem Server und dem Client auftreten würde. Deshalb müssen Sie die Status auf dem Client beibehalten, damit der Server alle Fehler beheben kann.
Entschuldigung für die schnellen Antworten, ich muss los. Lesen Sie diesen Artikel , er erwähnt Schützen, sollte aber für jedes Spiel funktionieren, das Echtzeit-Networking erfordert.
quelle