Ghost Replay - Speicherung und Timing

11

Ich arbeite an einem Autorennspiel und habe gerade ein Ghost Sprite implementiert, um vergangene Rennen wiederzugeben. Ich benutze eine Physik-Engine und bin nach langem Lesen zu dem Schluss gekommen, dass der beste Weg, die Geisterdaten für die Wiedergabe zu speichern, darin besteht, die Position und Drehung des Autos zu bestimmten Zeitpunkten aufzuzeichnen, wie hier beispielsweise beschrieben: https: // gamedev. stackexchange.com/a/8380/26261 .

Aber was wäre ein guter Weg, um diese Zeitpunkte während der Wiedergabe zu finden? Ein Beispiel wäre ein Datensatz mit diesen Daten:

time: +3.19932 (seconds since race start)
position:  180,40 (position at that time)
rotation: 30.4 (rotation at that time)

Aber ich habe einige Probleme damit:

  1. Wenn ich wiederhole, ist es unwahrscheinlich, dass ich den genauen Zeitpunkt um 3.19932 wieder erreiche. Wahrscheinlicher ist, dass ich einen Zeitpunkt um 3.1 habe und den am besten passenden Datensatz finden muss. Bei der Interpolation auch die engste Übereinstimmung oben und unten. Das klingt sehr ineffizient und zeitaufwändig?

  2. In welcher Listenstruktur könnte ich diese Datensätze für eine spätere Wiedergabe speichern? Eine Anordnung? Bedeutet das nicht, dass die Suchzeit für Datensätze, die einer bestimmten Zeit entsprechen, umso länger wird, je länger das Rennen dauert?

  3. Welche Frequenz soll ich für Zeitpunkte verwenden? Jeder Frame wäre - ich denke - übertrieben, eher sollte ich dh jeden n-ten Frame speichern und dazwischen interpolieren, was die Speicherfragen in 2. noch schwieriger macht.

Ist diese Idee überhaupt der richtige Ansatz? Wenn ja, wie kann ich die Daten effizient speichern und abrufen? Bitte beachten Sie, dass ich im Allgemeinen die obige Datenstruktur verwenden möchte, nicht deterministische Spielzustände und Benutzereingaben usw. aufzeichnen möchte.

Vielen Dank für jede Hilfe!

EDIT: Mir ist klar, dass ich die Umgebung beschreiben sollte, die ich benutze: Cocos2D für iPhone. Es gibt eine Methode update:(ccTime)delta. Idealerweise wird diese Methode alle 1/60 Sekunden aufgerufen, es gibt jedoch keine Garantie - deltaist die tatsächliche Zeit, die seit dem letzten Gametick vergangen ist, und kann viel mehr oder weniger als 1/60 betragen. Bei dieser Methode möchte ich den aktuellen Spielstatus speichern.

Marimba
quelle
2
Ausgezeichnete Frage. Wie dies zeigt, ist eine präzise Wiedergabe komplexer als Sie vielleicht zunächst denken, und ich bin gespannt, welche Lösungen die Leute hier gefunden haben.
Christian

Antworten:

8

Bedeutet das nicht, dass die Suchzeit für Datensätze, die einer bestimmten Zeit entsprechen, umso länger wird, je länger das Rennen dauert?

Nein :)

Angenommen, Sie speichern es als Array (beachten Sie, dass die Schnappschüsse in chronologischer Reihenfolge, aber nicht gleichmäßig verteilt sind):

snapshots = [
    {time: 0.0, position: {x,y,z}},
    {time: 0.41,    position: {x,y,z}},
    {time: 0.57,    position: {x,y,z}},
    {time: 1.10,    position: {x,y,z}},
    {time: 1.67,    position: {x,y,z}},
    {time: 2.05,    position: {x,y,z}},
    {time: 3.24,    position: {x,y,z}},
    {time: 3.86,    position: {x,y,z}},
    {time: 3.91,    position: {x,y,z}},
    {time: 5.42,    position: {x,y,z}},
    ...]

Wenn dann die Wiederholung / das Spiel beginnt, erhalten Sie das erste und zweite Element aus dem Array:

nextIdx = 1
previousSnapshot = snapshots[nextIdx-1]
nextSnapshot = snapshots[nextIdx]

Dann in jedem Frame ( currentTimeist die aktuelle Zeit in diesem neuen Spiel):

if currentTime > nextSnapshot.time
    nextIdx++
    previousSnapshot = snapshots[nextIdx-1]
    nextSnapshot = snapshots[nextIdx]

# Do your magic here, e.g.:
snapshotPairGap = nextSnapshot.time - previousSnapshot.time
ratio = (currentTime - previousSnapshot.time) / snapshotPairGap
ghostPosition = {
    x: previousSnapshot.position.x + ratio*(nextSnapshot.position.x - previousSnapshot.position.x)
    y: previousSnapshot.position.y + ratio*(nextSnapshot.position.y - previousSnapshot.position.y)
    z: previousSnapshot.position.z + ratio*(nextSnapshot.position.z - previousSnapshot.position.z)
}

Dies könnte natürlich durch Zwischenspeichern einiger Berechnungen optimiert werden. Es wird nicht im Array gesucht, sondern nur nach bestimmten Indizes gesucht.

Supr
quelle
JA! Ich muss das später ausprobieren, aber das scheint das zu sein, wonach ich gesucht habe. Vielen Dank!!
Marimba
15

Es ist nicht zu schwer. Sie können Ihre Daten zu beliebigen Zeitpunkten speichern (je mehr, desto besser) und die Werte der Daten basierend auf dem gesuchten Zeitstempel und den Daten von zwei am nächsten aufgezeichneten Zeitstempeln interpolieren, z.

N | Time | Position | Rotation
1 | 0.05 | 1, 1, 1  | 0
2 | 0.15 | 1, 2, 1  | 0
3 | 0.25 | 1, 3, 2  | 30

Stellen Sie sich nun vor, Sie möchten Position und Drehung zum Zeitpunkt 0.10 erhalten. Da 0,10 zwischen den Punkten '1' (was 0,05 Zeit bedeutet) und '2' (was 0,15 Zeit bedeutet) liegt, müssen Sie diese interpolieren.

timestamp = 0.10
factor = (timestamp - Time[1]) / (Time[2] - Time[1])
position = Lerp(Position[1], Position[2], factor)
rotation = Lerp(Rotation[1], Rotation[2], factor)

Lerpist nur lineare Interpolation .

Füllen wir also die Lücken mit einigen Beispielen (*).

N | Time  | Position    | Rotation
1 | 0.05  | 1, 1,    1  | 0
* | 0.075 | 1, 1.25, 1  | 0
* | 0.10  | 1, 1.5,  1  | 0
2 | 0.15  | 1, 2,    1  | 0
* | 0.20  | 1, 2.5,  2  | 15
3 | 0.25  | 1, 3,    2  | 30

HTH.

Marcin Seredynski
quelle
5
+1. Interpolation ist hier die einfache und effektive Antwort. Es mag sein, dass die kubische Interpolation beim Abbiegen des Fahrzeugs etwas bessere Ergebnisse liefert, aber linear funktioniert gut, wenn die Intervalle klein genug sind.
Kylotan
Vielen Dank, dass Sie gezeigt haben, wie man interpoliert! Dies wird für mein Spiel sehr nützlich sein. Angenommen, ich möchte zum Zeitpunkt 41.15 tief im Array abrufen. Würden Sie das gesamte Array durchsuchen, bis Sie einen Datensatz> 41,15 gefunden haben?
Marimba
1
Eine einfache lineare Suche könnte für Sie funktionieren, aber eine binäre Suche ist besser, wenn Sie ein sortiertes Array haben: en.wikipedia.org/wiki/Binary_search_algorithm
Marcin Seredynski