Wie implementiere ich Zeitreisen in ein Spiel?

10

Ich habe mich gefragt, wie ich Zeitreisen in ein Spiel implementieren kann. Nichts Superkomplexes, nur Zeitumkehr wie in Braid, wo der Benutzer die Zeit um 30 Sekunden zurückspulen / vorspulen kann oder was auch immer.

Ich habe viel im Internet gesucht, aber meine Ergebnisse bezogen sich normalerweise auf die Verwendung von Zeit wie "es ist 3:00" oder einen Timer und so weiter.

Das einzige, was ich mir vorstellen konnte, war, zwei Arrays zu verwenden, eines für die x-Position des Spielers und das andere für die y-Position des Spielers, und dann diese Arrays zu durchlaufen und den Charakter an dieser Position zu platzieren, während er zurückspult / schnell vorspult. Könnte das funktionieren? Wenn es funktionieren würde, wie groß müsste das Array sein und wie oft sollte ich das x und y des Players speichern? Was könnte ich sonst noch versuchen, wenn es nicht funktioniert?

Danke im Voraus!

Alex
quelle
11
Lesen Sie nicht die Nachrichten ( ust.hk/eng/news/press_20110719-893.html )? Sie haben gerade gezeigt, dass Zeitreisen nicht möglich sind. Daher ist es unmöglich zu codieren.
Sie müssen über eine mögliche Semantik für Zeitreisen nachdenken, erst danach können Sie über die Implementierung nachdenken.
Paŭlo Ebermann
2
Lernen Sie sich etwas Vektormathematik . Wenn Sie zwei separate Arrays vorschlagen, bedeutet dies, dass Sie noch nie mit ihnen gearbeitet haben. Ich halte sie für wichtig, damit ein Spieleprogrammierer weiß, wie sehr sie die Dinge vereinfachen können.
Doppelgreener
5
import universal.back2future.FluxCapacitor;
Schockierend

Antworten:

6

Die Array-Idee ist ziemlich genau so, wie sie in Braid implementiert wurde. Wenn die einzigen Dinge, die auf einen Charakter einwirken, die Schwerkraft, die Tastatur- / Joypad-Eingabe und andere Charaktere sind, müssen Sie so ziemlich nur die Position und die Geschwindigkeit bei jedem Schritt speichern, um alles Wichtige über diesen Charakter zu wissen. Wenn Sie 10 Schnappschüsse pro Sekunde und Charakter speichern, sind es für eine Minute des Verlaufs eines Charakters immer noch weniger als 50.000 - auf den meisten Systemen leicht zu verwalten, und Sie können auch effizientere Wege finden.

Kylotan
quelle
Dies ist der richtige Weg. Speichern Sie beispielsweise aktive Befehle: Tastendrücke mit Zeitstempeln. Sie können die meisten anderen Verhaltensweisen extrapolieren, wenn das System deterministisch ist. Ein 10-fps-Tracker ist für ein Rückspulsystem in Ordnung, noch weniger ist akzeptabel, solange die tatsächlichen Befehle beibehalten werden, was den Status oder die Statusänderungen der Objekte bewirkt.
Karmington
4

Informieren Sie sich über das Befehlsmuster . Es sieht vor, Aktionen rückgängig zu machen (und sie später erneut auszuführen). Dies würde mehr als nur die Position eines Schiffes behandeln, sondern alle Aktionen, die der Spieler ausführt.

Aber ich denke, Ihre Array-Idee ist auch solide.


quelle
Wahrscheinlich keine gute Idee - abgesehen davon, dass es speicherintensiver ist (jedes Mal, wenn Sie die Geschwindigkeit eines Objekts zu seiner Position hinzufügen, müssen Sie diese als "Aktion" speichern) , erfordert es ziemlich viel Festkomma-Mathematik, da, wenn Sie Floating verwenden -Punkt für Ihre Positionen / Geschwindigkeit, (x+v)-vmöglicherweise nicht gleich x.
BlueRaja - Danny Pflughoeft
1
@BlueRaja - Sehen Sie nicht, wo dieser Ansatz speicherintensiv wäre. Bei 100 FPS und Speichern der letzten 10 Sekunden müssen Sie 1000 n-Tupel von Befehlen speichern, wobei n höchstens 5 ist. Oder, noch besser, Sie speichern nur die absolute Position in jedem dieser Frames und animieren zum Zurückspulen den Charakter einfach rückwärts auf diesem Pfad. Dies würde auch mögliche Gleitkommaprobleme beseitigen und Sie genau dorthin zurückbringen, wo Sie begonnen haben.
Hackworth
3

Anstatt zwei separate Arrays zu haben, sollten Sie wahrscheinlich eine Klasse haben, die die Position des Spielers beschreibt (es gibt wahrscheinlich bereits eine Point-Klasse in Java ... Ich bin in letzter Zeit ein C # -Typ) und ein einziges Array verwenden, um vergangene Positionen zu halten.

Sie müssen einen "Ringpuffer" einrichten, dh, wenn Sie am Ende des Arrays angelangt sind, kehren Sie zum Anfang des Arrays zurück und überschreiben die ältesten Einträge. Wenn Sie in der Zeit zurückreisen, ist das Gegenteil der Fall (wenn Sie am Anfang angelangt sind, kreisen Sie bis zum Ende ein).

Wenn Sie vergangene Daten im Wert von 30 Sekunden speichern möchten, müssen Sie Ihre Bildrate kennen, wenn Sie Speicherplatz vorab zuweisen und ein Array mit fester Größe verwenden möchten. Wenn Sie das Spiel mit 10 Bildern / Sekunde und 30 Sekunden rendern, sind das 300 Elemente.

Eric J.
quelle
2

GDCVault hat einen Vortrag von Jon Blow (dem Schöpfer von Braid ) auf seiner Website mit dem Titel The Implementation of Rewind in Braid für 3,95 US-Dollar. Ich wette, das hat die Infos, die du willst;)

EDIT: Wird wahrscheinlich nicht in Java sein, aber die Ideen sollten halten.

NoobsArePeople2
quelle
Wissen Sie, ob sie auch das Transkript oder nur das Audio verkaufen?
o0 '.
Durch eine schnelle Suche auf der Website konnte ich nichts finden. Vielleicht könnten Sie eine Transkriptionssoftware verwenden ?
NoobsArePeople2
1

Wie Erik J sagte , klingt es vernünftig, die früheren Positionen des Spielers als Sammlung von Punktobjekten in einem Ringpuffer zu speichern.

Ich würde jedoch vorschlagen, eine Warteschlange zum Implementieren des Puffers zu verwenden. Das Aktualisieren ist viel billiger als ein Array, und Sie müssen Ihre Bildrate nicht im Voraus kennen:

update():
   time_queue.push(player.positions)
   if current_time() - start_time > n:
       queue.pop()

Dies berücksichtigt noch keine unterschiedlichen Bildraten oder was passieren sollte, wenn Sie tatsächlich Zeitreisen unternehmen. Ich empfehle daher, bei jedem Eintrag einen Zeitstempel zu speichern und stattdessen Folgendes zu überprüfen:

update():
    time_queue.push({
        'pos': player.positions,
        'time': current_time()
    })
    first_entry = queue.peek()
    while current_time() - first_entry['time'] > n:
       queue.pop()
       first_entry = queue.peek()

Hoffe das hilft!

HumanCatfood
quelle
1

Es gibt ein Designmuster namens Memento , ich denke, es ist ein Ausgangspunkt für ein Spiel wie Braid

Das Erinnerungsmuster ist ein Software-Entwurfsmuster, mit dem ein Objekt in seinen vorherigen Zustand zurückversetzt werden kann (Rückgängigmachen per Rollback).

Das Erinnerungsmuster wird von zwei Objekten verwendet: dem Urheber und einem Hausmeister. Der Urheber ist ein Objekt, das einen internen Zustand hat. Der Hausmeister wird dem Urheber etwas antun, möchte aber in der Lage sein, die Änderung rückgängig zu machen. Der Hausmeister bittet den Urheber zunächst um ein Erinnerungsobjekt. Dann führt es die Operation (oder Abfolge von Operationen) aus, die es ausführen wollte. Um vor den Operationen in den Status zurückzukehren, wird das Erinnerungsobjekt an den Absender zurückgegeben. Das Erinnerungsobjekt selbst ist ein undurchsichtiges Objekt (eines, das der Hausmeister nicht ändern kann oder sollte). Bei Verwendung dieses Musters sollte darauf geachtet werden, dass der Urheber andere Objekte oder Ressourcen ändert - das Erinnerungsmuster wird für ein einzelnes Objekt ausgeführt.

http://en.wikipedia.org/wiki/Memento_pattern

Weitere Informationen hier: http://dofactory.com/Patterns/PatternMemento.aspx

Marcelo Assis
quelle
2
Ich sehe nicht ein, wie das helfen soll. Es sieht so aus, als wäre es ein "Instant-Rollback", während das OP nach etwas Glattem wie dem von Braid fragte. Vielleicht habe ich etwas falsch verstanden?
o0 '.
Dies ist ein Muster, das bei einem einzelnen Rollback nicht impliziert wird. Sie können beispielsweise eine "Zeitleiste" von Aktionen erstellen. Hier ist ein Blogbeitrag von Brazillian über diese Verwendung dieses Musters: abrindoojogo.com.br/padroes-de-projeto-memento Hier ist ein Beispiel aus Flash: abrindoojogo.com.br/files/fasteroids.swf Move: Arrows | Schießen: Raum | Rücklaufaktionen: Rücktaste
Marcelo Assis
1

Für die XBox360 wurde ein Spiel veröffentlicht, das Zeitmanipulationen beinhaltete. Es war mittelmäßig, daher kann ich mich derzeit nicht an den Titel erinnern. Wie auch immer, in einem Interview mit einem Entwickler haben sie dargelegt, wie sie mit Zeitmanipulation umgegangen sind:

In jedem X-Frame (mit niedrigeren X-Werten, die zu einer feinkörnigeren Manipulation führen) erstellen Sie zu diesem Zeitpunkt einen "Schnappschuss" der Spielwelt und verknüpfen ihn mit einer Zeit im Spiel.

Während Sie das Spiel normal und zeitlich vorwärts durchspielen, trägt jede Eingabe und Reaktion zu einem späteren Zeitpunkt zum Schnappschuss bei.

Die Spielwelt iteriert dann zwischen dem aktuellen Snapshot und den zukünftigen Snapshot-X-Frames.

Wenn Sie dann die Zeit umkehren möchten, stellen Sie einfach die Zeitrichtung so ein, dass sie rückwärts ist, sodass sie in der Vergangenheit zwischen dem aktuellen und dem Snapshot-X-Frame iteriert (während die Möglichkeit deaktiviert wird, zukünftige Snapshots zu erstellen).

Jordaan Mylonas
quelle
+1 für den Punkt über die Granularität der Schnappschüsse.
Omar Kooheji
0

Sie können die virtuelle Zeit des Spiels einfach als eine andere raumähnliche Dimension behandeln. Was also Zeitreisen von außen sind, ist eine einfache n + 1-dimensionale Bewegung im virtuellen Universum des Spiels.

Unter der Annahme einer Benutzerinteraktion und einer Art Physiksystem, das das Verhalten Ihres Universums bestimmt, wenn keine Benutzereingaben vorliegen, müssen Sie lediglich den Effekt von Benutzerinteraktionen aufzeichnen (z. B. Änderungen der Geschwindigkeits- / Beschleunigungsvektoren der Dimension n + 1). , da Ihre Physik zeitlich reversibel sein sollte.

Auf diese Weise benötigen Sie viel weniger Speicher, um den Status des Spieluniversums zu einem bestimmten Zeitpunkt zu berechnen.

biziclop
quelle
Wie ich in einem anderen Kommentar erwähnt habe, ist die Physik in Videospielen normalerweise nicht(x+v)-vx
zeitumkehrbar
0

Angenommen, eine Entität hat eine Geschwindigkeits-, Rotations-, x- und y-Position. Dies sind mit Beschleunigung die grundlegenden Bewegungszustände, Werte, wie auch immer Sie es nennen. Sie können die Daten auf zwei Arten
speichern : 1. Speichern Sie Rotation, x- und y-Position.
2. Speichern Sie Rotation, x-Geschwindigkeit und y-Geschwindigkeit

Wenn Sie eine feste Geschwindigkeit haben, können Sie auch nur die Drehung oder nur eine Geschwindigkeit für beide Achsen speichern.

Das Speichern der Rotation ist in Spielen erforderlich, es sei denn, Ihre Entität verfügt über eine statische Rotation, was in den meisten Fällen nicht der Fall ist.

Die Verwendung einer Liste von Objekten für mehrere Werte ist jedoch erforderlich. Wenn Sie ein nettes Timer-System haben, das beispielsweise 20 Mal pro Sekunde Aktualisierungsmethoden aufruft und daher fps-unabhängig ist, können Sie ein Array von 20 * 30 Objekten erstellen, um die Bewegungswerte zu speichern, die Sie für die letzten 30 Sekunden benötigen . Die Verwendung eines einfachen Arrays wird nicht empfohlen, da Sie jedes Element um einen Index verschieben müssten, der bei jedem Aktualisierungsaufruf übrig bleibt.

Auf dieser Grundlage können Sie die Liste oder was auch immer durchgehen, um zurück zu gehen. Wenn Sie wirklich einen "realistischen" Effekt erzielen möchten, verwenden Sie diese Technik für alle sich bewegenden Objekte. Aber das hängt mit dem Spieldesign zusammen.

Wenn Sie Objekte zerstören, denken Sie daran, diese zu bündeln, um sicherzustellen, dass Sie Ihren netten Freund Mr. Garbage nicht belasten;)

Marco
quelle