hallo Jungs,
ich hab meine Bachelorarbeit über das Thema gemacht, allerdings für eine Firma und ich darf die Arbeit nicht veröffentlichen. Aber hier mal ein Überblick was es da für Techniken gibt:
1. Client-Side Prediction mit Interpolation
So macht es Counterstrike, deshalb ist es auch so schön präzise. Wenn ein Client losläuft sieht er das sofort bei sich auf dem Rechner, ohne Verzögerung. Gleichzeitig schickt er ein Paket an den Server los, mit einem Zeitstempel wann er die Vorwärts-Taste gedrückt hat.
Der Server sammelt die Pakete von allen Clients und hat so ein paar ms später immer das korrekte Bild vom Spielgeschehen, und verteilt diese Info weiter an alle Spieler. Der Server hat immer Recht: Falls Pakete verloren gegangen sein sollten und er nicht mitbekommen hat, wo du mittlerweile hingelatscht bist, setzt er dich an die jeweilige Position zurück ("Laaaaaag !"). Deshalb heißts "Prediction": Du sagst bei dir lokal voraus, wo der Server dich beim nächsten Positionsupdate hinsetzen wird, basierend auf den gedrückten Tasten und deiner aktuellen Position / Geschwindigkeit. Solange kein Datenverlust auftritt, wird diese Vorhersage immer stimmen.
Der Server sammelt mehrere dieser Positionsupdates, und gibt sie dann verzögert an die Clients raus, die dann zwischen den Positionsangaben interpolieren. Also wie eine Spur von Brotkrümeln, die die Spielobjekte bei dir lokal dann einsammeln. Dass erst mehrere Krümel abgewartet werden, bevor ein Spielerobjekt bei dir überhaupt erst losläuft hat den Sinn, dass auch mal ein Update verloren gehen oder stark verzögert werden kann, und die Darstellung ist immer noch geschmeidig. In der Praxis bei ner soliden Inetverbindung würde ich die Verzögerung auf die 2fache Gesamtlatenz setzen (also 2 x Client->Server->Client) setzen.
Durch die Latenz und diesen Verzögerungspuffer siehst du die Bewegung der anderen Spieler immer verzögert. Wenn du auf einen Spieler ballerst, ist der dementsprechend in Wirklichkeit schon ganz woanders ("schön durch die Kiste erschossen, der hat doch an !!"). Das macht bei Counterstrike aber in Sachen Fairness nichts, weil da jeder Schuss sofort trifft, und es keine Projektil-Objekte gibt, denen der andere noch ausweichen könnte. Wichtig ist nur, dass ihr den anderen in eurer lokalen Ansicht getroffen habt. Der Server rekonstruiert eure Ansicht (er hat ja alle Zeitstempel und Positionen der Vergangenheit) und weiß, wann ihr wen getroffen habt.
Die Position der Spieler spielt also bei CS nur eine Rolle, wenn die Spieler untereinander kollidieren und sich blocken. Die Spieler selbst bewegen sich bei CS aber langsam genug, dass diese Abweichung zu klein ist, um das Spielgeschehen zu beeinflussen. Auch Latenzen von 120 ms und mehr merkt man eigentlich nicht.
Also Fazit: Wenn sich alle relevanten Spielobjekte langsam genug bewegen, kann ich das nur empfehlen. Die meisten Shooter haben allerdings einen Raketenwerfer u.ä., da muss man dann mit anderen Techniken tricksen (siehe Dead Reckoning / Local Lag)
+ sehr präzise in der Darstellung, auch bei hohen Latenzen
- starke Abweichung zur tatsächlichen aktuellen Position der Spielobjekte
- braucht einen konstante, relativ hohe Updaterate (ca alle 50ms), viel Traffic
2. Dead Reckoning 1. Ordnung
Diese Technik ist in der Spielewelt am weitesten verbreitet. Sie berücksichtigt die Positionen der Spieler und deren Geschwindigkeit. Im Gegensatz zur Interpolation braucht man keine konstante Rate an Positionsupdates, sondern man muss im Prinzip nur einmal pro Richtungsänderung eine Nachricht an den Server schicken. Dadurch fällt normalerweise extrem wenig Traffic an im Vergleich zu Client Side Prediction mit Interpolation.
1. Ordnung heißt, dass nur die Geschwindigkeit mit einbezogen wird. Es gibt noch 2. Ordnung, dabei wird dann auch die Beschleunigung berücksichtigt. Alle Multiplayerspiele die wir untersucht haben, beschränken sich allerdings auf 1. Ordnung, weil im Falle von Lag/Paketverlust der Fehler sehr schnell sehr groß werden kann, und 1. Ordnung normalerweise auch präzise genug ist.
Also beim Standardansatz von Dead Reckoning schickst du wieder bei einer Richtungsänderung deiner lokalen Spielfigur ein Update an den Server. Der verteilt das sofort an die anderen Spieler weiter, und die setzen dich auf sofort nach Empfang auf die aktuellste Position, die sie gerade empfangen haben. Weil dabei auch die Geschwindikeit mitgeschickt wird, können sie anhand dieser Info dann ausrechnen, wo du bis zum nächsten Positionsupdate in jedem Frame ungefähr sein wirst. Aufgrund der Latenz wird diese Position zum Zeitpunkt der nächsten Richtungsänderung aber nie genau übereinstimmen. Um das zu kaschieren kann man dann von der lokal (geschätzten) Position zur neuen tatsächlichen Position hin-interpolieren. Das führt dann allerdings ggf zu einem kantigen Bewegungsverlauf, deshalb gibt's noch eine Variante davon: Man errechnet anhand der aktuellen Geschwindigkeit den Schnittpunkt mit der Bewegungsrichtung, um dann dahin zu interpolieren. Dadurch ist die durchschnittliche Abweichung zur tatsächlichen aktuellen Position zwar insgesamt schlechter, aber man vermeidet Sprünge in der Darstellung.
+ wenig Traffic
+ einigermaßen gute Übereinstimmung zur serverseitigen Position
- Gefahr von Sprüngen im Bewegungsverlauf
3. Dead Reckoning mit Time-Compensation
Im einfachen Dead Reckoning wird also jedes Spielobjekt beim Update jeweils ungefähr da angezeigt, wo man die letzte Position empfangen hat. Diese Information ist aber in jedem Fall verzögert um die Latenz von dessen Client zum Server, plus die Latenz vom Server zum lokalen Client. Das kann man ausgleichen, indem man Zeitstempel mitschickt: Wenn man weiß, wie alt die Information ist, kann man in Verbindung mit der Position und der Geschwindigkeit ja hochrechnen, wo sich das Objekt jetzt gerade im Moment befindet. Das funktioniert im Prinzip gut, allerdings kann man bei schnellen Richtungsänderungen bzw hohen Latenzen auch sehr falsch damit liegen. Das führt dann zu extremen Sprüngen in der Darstellung, auch wenn die Position in der lokalen Darstellung dann insgesamt besser übereinstimmt. In der Praxis ist es also gesund, da einen Mittelweg zu finden, und nicht über die volle Latenz hochzurechnen. Das hängt natürlich auch von deinem Bewegungsmodell ab, also wie ruckartig man in deinem Spiel die Bewegungsrichtung ändern kann.
+ wenig Traffic
+ sehr gute Übereinstimmung zur serverseitigen Position
- große Gefahr von Sprüngen im Bewegungsverlauf
4. Local Lag
Noch ein Ansatz, die durchschnittliche Abweichung in der Darstellung klein zu halten ist natürlich, die ganze Aktion schon lokal zu verzögern. Also radikalster Ansatz: Der Spieler bewegt sich auch auf seinem eigenen Rechner nicht, bis man die tatsächliche Antwort vom Server erhalten hat. So wirds z.B. bei Echtzeit-Strategiespielen gemacht, wo die genaue Position der Einheiten sehr wichtig ist, und es umgekehrt relativ wurscht ist, dass die Einheiten dann nach dem Klick nicht sofort loslaufen. Bei Shootern leidet das Spielgefühl aber natürlich extrem. Local Lag empfielt sich da bei einigen Aktionen aber trotzdem: Wenn z.B. ein Raketenwerfer abgefeuert wird, ist es sehr wichtig, dass bei allen Beteiligten die Darstellung möglichst gut übereinstimmt, damit man zielen bzw. ausweichen kann. Da sollte der Fehler also möglichst klein sein, und deshalb arbeitet Unreal Tournament z.B. da auch mit Local Lag: Das Mündungsfeuer wird lokal zwar sofort angezeigt, aber das Projektil fliegt erst los, nachdem der Server die "abgefeuert"-Nachricht bestätigt hat. Die Positionsabweichung der Spieler ist aber nicht so ohne weiteres auszugleichen. Bei UT kann es deshalb trotzdem passieren, dass man lokal das Projektil an sich vorbeifliegen sieht, der Server das aber anders sieht, und kurz danach liegt man tot am Boden. Umgekehrt genauso: Leute werden in der lokalen Darstellung vom eigenen Projektil erwischt, aber in Wirklichkeit hat man knapp verfehlt ("Cheater!"). Wie bei Time Compensation auch kannst du da nur selber ausprobieren, was für welche Aktion in deinem Spiel das beste ist, und mit dem Verzögerungswert rumexperimentieren. Es ist immer ein Tradeoff zwischen 'sieht gut aus' vs. 'spielt sich flüssig' vs. 'ist fair und transparent für alle'.
+ eröht die Übereinstimmung zur serverseitigen Position
- verschlechtert das Spielgefühl
Hinweis für Zeitstempel: Ihr braucht eine gemeinsame Zeitreferenz, z.B. die Serverzeit als vergangnene Millisekunden seit Level-Start. Damit die nicht durch die unterschiedlichen Latenzen der Clients zueinander verfälscht wird, müsst ihr am Anfang einmal ausrechnen, wie um wieviele ms die lokale Uhr von jedem Client zu der des Servers abweicht. Am besten damit:
http://de.wikipedia.org/wiki/Algorithmus_von_Cristian
viele Grüße,
ponx