Einzelnen Beitrag anzeigen

blackfin
(Gast)

n/a Beiträge
 
#3

AW: 2D Netzwerkspiel grundlegende Frage

  Alt 23. Aug 2010, 08:26
Jetzt hab ich fast ein Déjà-vu, etwas Ähnliches habe ich ja erst selbst vor ein paar Tagen gefragt
Nach weiterer Recherche im Internet kann ich nun sagen, dass bei einem Spiel an den Server vom Client im Normalfal eigentlich nur folgendes gesendet wird:

- Position der Figur bei Bewegungs-Start
- Bewegungsrichtung
- Bewegungsart (laufen / gehen / drehen etc.)
- Bewegungs-Aktion (start / stop)

Der Client läuft selbst lokal los und schickt diese Informationen an den Server.
Der Server kennt selbst die Geschwindigkeiten der Bewegungsart und ermittelt anhand der Angaben alle paar Sekunden die neue Position der Figur.
Dese wird dann ebenfalls alle paar Sekunden an den sendenden Client und die Clients gesendet, die in Sichtweite der Figur sind und mit diesen synchronisiert, dabei hat der Server das Sagen.
Hält der Client die Bewegung an, wird ein Bewegungs-Stop an den Server gesendet. Hierbei wird dann final die Position ermittelt und wieder mit dem Client synchronisiert.

Beispiel:
1) Client-Figur soll gegen den Uhrzeigersinn nach Norden gedreht werden, der Spieler drückt also z.B. "A".
Der Client fängt an, sich lokal (über die Client-seitige Game-Engine) zu drehen und sendet folgendes an den Server:
Client => Server:
Opcode: START_PLAYER_ROTATION
Parameter 1: PLAYER_DIRECTION
Parameter 2: ROTATION_CCW

----
2) Jetzt wird, solange der Spieler "A" gedrückt hält, alle paar Sekunden die Drehrichtung vom Server aus mit den Clients synchronisiert, die in Sichtweite sind (inklusive dem sendenden Client):

Server => Clients, die in Sichtweite sind
OpCode: PLAYER_ROTATION_SYNC
Parameter 1: NEW_PLAYER_DIRECTION

----

3) Figur ist nach Norden ausgerichtet, der Spieler lässt "A" wieder los.
Client => Server:
Opcode: STOP_PLAYER_ROTATION
Parameter 1: PLAYER_DIRECTION

-----
4) Jetzt wird die finale Drehrichtung vom Server ermittelt und an die relevanten Clients gesendet, die das Objekt (die Figur) dann auf diese Drehrichtung setzen, auch wenn diese von der lokalen abweicht.

Server => Clients, die in Sichtweite sind
OpCode: PLAYER_ROTATION_SYNC
Parameter 1: NEW_PLAYER_DIRECTION

------

Durch diese Verhaltensweise kommt es eben zu den bekannten "Client-Sprüngen", wenn der Server mit dem Client syncronisiert, so dass z.B. die Figur schlagartig eine andere Drehrichtung besitzt.
Das gleiche passiert natürlich auch bei Bewegungen.
Gerade bei der Drehung von Objekten kann es aber auch durchaus sein, dass hier bei der finalen Postionierung der sendende Client das Sagen hat, da eine Drehung meist in einem so kurzen zeitlichen Rahmen passiert, dass die Synchronisation stottern würde, falls der Server das Sagen hat. Das ist je nach Spiel und Situation selbst festzulegen. Bei Translationen allerdings heisst es eigentlich generell immer: Das, was der Server berechnet hat, gilt.
Mein Beispiel bezieht sich jetzt zwar auf die Bewegung in 3D, jedoch gilt das gleiche auch für 2D, nur dass halt hier eine Raumachse wegfällt, ansonsten ist es ja das gleiche

Das generell kompilzierte beim Server ist, auf dem Server selbst die Spiel-Logik ohne viel Ballast einzubauen, die auch korrekt die Wegstrecken / Drehungen usw. berechnen kann, ohne dass der Server ja selbst eine Anzeigefläche besitzt und viele Dinge wie z.B. Kollisionserkennung nur "virtuell" enthält, im Gegensatz zum Client, der ja eine Anzeigefläche und "reale" Objekte hat.
Dafür werden meist beim Server sogenannte "Maps" generiert, die vorberechnet Daten über die Umgebung beinhalten, auf die schneller zugegriffen werden kann als bei der Live-Prüfung des Clients.
Hier wird jedoch meist noch viel auf den Client selbst ausgelagert, die Kollisionsabfragen z.B. sind bei den meisten Spielen immer noch auf der Client-seite, da serverseitig nur ziemlich kompliziert zu berechnen. Dadurch kommt es auch, dass es so viele "Wall-Hacks" für z.B. MMO's gibt.
Der Server prüft meist nur in einem recht breiten Rahmen, ob die Client-Anfrage überhaupt gültig ist und korrigiert ggf. zu schnelle Bewegungen oder zu schnelle Schusswiederholraten usw.
Als Problem zeigt sich hierbei noch die Paket-Latenz zwischen Client und Server, die kompensiert werden muss.

Die Ermittlung, welche Clients bei der Nachrichten-Verteilung relevant sind, wird serverseitig meist mit Client-Gruppen realisiert.
Wenn eine Positionsänderung eintritt, ermittelt der Server die Clients in der Nähe und bildet aus diesen eine Client-Gruppe (in Delphi könnte dies z.B. eine TObjectlist sein).
Diese wird durch eine Bewegungs-Änderung einer der beinhalteten Clients verändert (Clients hinzugefügt / entfernt).
Diese Gruppe bildet dann sozusagen eine "Message-Gruppe" für Events der Clients untereinander, wobei natürlich die Clients nicht direkt zueinander senden können, sondern alles über den Server zwecks Prüfung abgewickelt wird.
Die Clients in Sichtweite werden meistens selbst bei einem 3D-Spiel aus Geschwindigkeitsgründen in 2D ermittelt, wobei die Höhenachse ignoriert wird (ausser, diese ist wirklich relevant, z.B. bei einem Weltraum-Game). Somit bleibt einfach die Position der Figur mit einem Sichtradius übrig, bei dem auf Schnittmengen der Clients in der gleichen Zone geprüft wird. Braucht man 3D, dann nimmt man einen Kugel-Radius, statt einem Kreis. Oft wird auch erstmal in 2D ermittelt und bei positiver Schnittmenge noch die 3. Achse geprüft.
Diese Prüfung findet aber nur dann statt, wenn sich ein Client in der gleichen Zone bewegt hat oder einer hinzugefügt wird.
Optimierungen gibt es hier viele, z.B. die Realisierung von Zonen über virtuelle Octrees oder die Priorisierung und das Gruppieren von Message-Queues / Client-Threads untereinander, die in einer gegeben Zeitspanne innerhalb einer bestimmten Gruppe lagen.
(Schlagwort: Spielen als Gruppe in Instanzen oder Ähnliches)

Zum Zeichnen der Objekte am Client:

Egal ob 2D oder 3D, Objekte werden bei Bewegungsänderungen an sich nicht an der neuen Position "gezeichnet", sondern an eine neue Stelle geschoben (Translation). Natürlich wird dann intern in der Engine neu gezeichnet...
Der Server sendet den Clients nach Prüfung auf Sichtweite Events, in denen beim Client ein neues Objekt erzeugt oder eines entfernt werden soll oder ob sich ein Objekt bewegt / verschiebt. Dazu braucht man auf der Client-seite natürlich erstmal einen Bewegungs-Controller, den man mit Befehlen füttern kann (z.B. Nimm Objekt x und bewege es mit Geschwindigkeit y in Richtung z).
Doch gibt es generell zwei grundlegend verschiedene Ansatzpunkte, um eine Animations-Engine zu verwirklichen:

I) Punkt-/Vertexbasiertes Animieren:

Animationen sind dabei Objekt-Intern und sind Punkt-basierend. Animationsüberlagerungen werden dabei meist über Kombinationen der Punkt-Animationen ausgeführt. So kann sich eine Figur z.B. gleichzeitig nach vorne bewegen (Lauf-Animation) und springen (Spring-Animation). Ist man hier im 3D-Raum, so werden über den Bewegungs-Controller die Bewegungen der Knochenpunkte (Bone-Joints) für alle gerade aktiven Animationen addiert und interpoliert.
Erklärung hierzu: Eine Spieler-Figur besteht in 3D meist aus dem 3D-objekt selbst (Mesh), dem ein Knochengerüst zugrundeliegt, das die "Verzerrung" des Mesh bei Bewegungen steuert. Bewegt sich ein Knochen, so werden über die Gewichtung der Knochen-Knotenpunkte die Vertices der Mesh-Aussenhaut verschoben . Um eine Animation zu erzeugen, bewegt man also im 3D-Programm die Knochen für jeden Bewegungs-Schritt und erstellt dann eine Liste von Positionen für eine Animation, die man dann von der Game-Engine abrufen kann. Diese interpoliert dann zwischen den Positionen, um eine flüssige Bewegung zu erhalten.
Vom Server kommen dann nur noch Befehle ala START_ANIMATION_RUN, springt der Spieler während des Laufens, kommt vom Server START_ANIMATION_JUMP, diese wird der aktiven Laufanimation hinzugefügt, wobei eine Kombination aus Lauf- und Sprunganimation entsteht.
Der Vorteil dieser tehcnik liegt auf der Hand: Man muss nur einmal das Objekt modellieren, kann durch Knochenbewegung alle möglichen Animationen zusammenbasteln und kann die einzelnen Animationen beim Anzeigen auch noch überlagern lassen, wodurch eine riesige Anzahl von unterschiedlichen Bewegungen entstehen kann.

Für dein Vorhaben, ein Bild zu verschieben, brauchst du das alles aber erstmal gar nicht, da es ja keine Animationen "im" Bild gibt, sondern nur das Bild an sich verschoben wird. Die Erklärung hierzu soll in erster Linie als Ausblick dienen, falls es eben nicht bei einem Verschieben von Bildern bleibt und du eventuell Punkt-vertexbasiert arbeiten willst. Das ist auch bei der Konzipierung zu überlegen, wie man Animationen eben realisieren will.
Kommen wir aber zu Animations-Variante 2, die in deinem Fall wohl eher zutrifft:

II) Sprite-basiertes Animieren:
Eine Sprite-Animation besteht aus umschaltbaren Abfolgen von Einzelbildern und beinhaltet keine Punkte-Verschiebung / Verzerrung am Objekt selbst.
Sobald das Bild verschoben / bewegt wird, wird auch die Stand-Animation des Bildes umgeschaltet auf eine Lauf-Animation. Sprich: Kommt vom Server ein BEWEGUNG_START, schaltet das Bild auf eine andere Animations-Reihenfolge um, bei einem BEWEGUNG_STOP wieder zurück auf die Stand-Animation.
Dafür brauchst du erstmal eine Sprite-Engine, die dieses Umschalten und animieren handlen kann. (Andorra 2D z.B.)
Das Problem beim Sprite-basierenden Animieren ist die Tatsache, dass man für jegliche Bewegungs-Kombination eine eigene Abfolge von Einzelbildern braucht, da man keine Punkte hat, dieman verzerren / verschieben kann und somit Kombinationen von Bewegungen unmöglich sind, ohne eine eigene Animation dafür zu zeichnen.
So werden ja auch z.B. herkömmliche Zeichentrickfilme animiert, jedes Bild ist Handarbeit.

III) Kombination aus Vertex-basiertem Animieren und Sprites:
Natürlich kann man die beiden Techniken auch kombinieren. Man bastelt sich ein Modell in einem 3D-Program mit Knochengerüst und exportiert die einzelnen Animationen als Einzelbilder für die Sprite-Engine.
Das ist heutzutage eigentlich der übliche Weg für ein 2D-Spiel.


Zu UDP/TCP:
UDP wird bei aktuelleren Spielen übers Internet nur noch für relativ "unwichtige" Informationen verwendet, die regelmäßig versendet werden und bei denen es nicht wichtig ist, dass sie wirklich jedesmal richtig ankommen (z.B. Client-/Serverzeit-Sync, Latenz-Ermittlung) und somit auch nicht aufwändig geprüft / verifiziert werden müssen.
Für wirklich relevante Dinge wie Bewegungen und der Spiel-Logik allgemein wird allerdings fast immer TCP verwendet, da der zeitliche Overhead der Daten-Strukturierung / Verifizierung bei UDP meist grösser ist als der native Geschwindigkeits-Vorteil gegenüber TCP.
Dies gilt, wie gesagt, für Spiele übers Internet, bei LAN kann man, wie Medium bereits sagte, UDP meist ohne grosse Probleme auch dafür verwenden.
Modernere MMO's oder auch Shooter benutzen somit meist eine Mischung aus UDP und TCP. UDP für unwichtiges, TCP für wichtiges.


Fazit:
Lange Rede, kurzer Sinn.
Bevor du dir über die Netzwerk-Technik überhaupt Gedanken machen kannst, brauchst du als aller Erstes auf der Client-Seite einen Bewegungs-Koordinator/Controller für Animationen, so dass du vom Server überhaupt Befehle wie ANIMATION_1_START und ANIMATION_1_STOP empfangen und auswerten kannst.
Dieser ist entweder Sprite-basierend oder Punkt-vertexbasierend, was eine grundlegende Entscheidungsfrage ist.
Diese Animations-Engine kannst du dann erst einmal lokal implementieren und testen.
Wenn der Controller funktioniert, kannst du dann auf die Serverlogik gehen und nach obigen Punkten überlegen, was der Server zu prüfen und zu senden hat, damit die Clients immer schön synchron animieren.

Ich weiss, das war nun viel Text und hat mit deiner simplen Frage nur noch in Teilen zu tun. Ich hoffe, es nützt dir trotzdem etwas

Geändert von blackfin (23. Aug 2010 um 13:09 Uhr)
  Mit Zitat antworten Zitat