Delta-Zeit und Bildfrequenz-Unabhängigkeit

0
  • 0 favoris

Spiele, die gleich schnell auf Computern mit unterschiedlichen Bildfrequenzen laufen, nennt man Framerate independent. Zum Beispiel, ein Spiel kann auf einem langsamen Rechner mit 30 fps (frames per second oder Bilder pro Sekunde) laufen, während es auf einem schnellen Rechner mit 60 fps läuft. Ein von der Bildfrequenz unabhängiges Spiel hingegen scheint auf beiden Rechnern genauso schnell zu laufen (Objekte bewegen sich scheinbar mit der gleichen Geschwindigkeit). Auf der anderen Seite wird ein von der Bildfrequenz abhängiges (framerate dependent) Spiel auf dem langsamen Rechner halb so schnell laufen wie auf dem schnellen. Es wirkt bei Ersterem wie ein Slow-Motion Effekt. Für den Spielspaß ist es unglaublich wichtig, den Aspekt der Unabhängigkeit der Bildfrequenz bei der Spieleprogrammierung zu berücksichtigen. SpielerInnen können aufgrund eines "lahmen" Spiels frustriert die Motivation verlieren.

Dieses Tutorial beschreibt, wie Du deine Spiele unabhängig der Bildfrequenzen programmieren kannst. Mit der hier beschriebenen Technik der Zeitskalierung (time scaling) kann man zusätzlich sowohl Slo-Mo Effekte als auch Pausen im Spiel einbauen.

Der System-Ausdruck

dt

Der Schlüssel der Bildfrequenz-Unabhängigkeit ist der System-Ausdruck dt. Dieser steht für die Delta Zeit (delta-time). Delta bedeutet ein Wechsel in der Quantität oder Menge, d.h. delta-time beschreibt den Zeitwechsel im Verlauf des Spiels. Es ist die Zeit in Sekunden seit dem letzten Tick.

Ein dt hat bei 100 fps den Wert 0.01 (ein hundertstel Sekunde) und bei 10 fps wird dt 0.1 betragen (ein Zehntel einer Sekunde). In der Praxis wird dt bei jedem Tick-Wechsel einen leicht anderen Wert haben, also wird es unwahrscheinlich sein, dass es in einer langen Zeit dt seinen Wert beibehält.

Addiert man bei jedem Tick dt zu einer Variable, wird nach jeder Sekunde immer eine 1 hinzuaddiert. Die Zeit zwischen all den Ticks in einer Sekunde muss sich ja zu 1 addieren! Hier ist ein Beispiel der dies zeigt. (Addiert man dt zu einer Instanzvariable eines Objektes ist eine praktische Art, einen Timer im Objekt zu haben.)

Wie man

dt benutzt

Typischerweise wird eine Bewegung in einer Bildfrequenz-Abhängigkeit in einem Event folgendermaßen gemacht:

Bei jedem Tick (einmal je Bild) bewegt sich das Objekt um einen Pixel nach rechts. Bei 30 fps bedeutet das 30 Pixel pro Sekunde und bei 60 fps wären das 60 Pixel pro Sekunde. Das sind unterschiedliche Geschwindigkeiten aufgrund der unterschiedlichen Bildfrequenzen.

Im obigen Beispiel addiert sich dt zu 1 in einer Sekunde. Ändert man den Event auf das folgende:

…bewegt sich das Objekt in jeder Sekunde um 60 Pixel nach rechts. Da sich dt in einer Sekunde zu 1 addiert, addiert sich 60 [] dt* zu 60 in einer Sekunde. Das heisst, dass egal ob das Spiel mit 30 fps oder 60 fps läuft, bewegt sich das Objekt immer 60 Pixel pro Sekunde nach rechts - gleiche Geschwindigkeit bei unterschiedlichen Bildfrequenzen!

dt sollte überall eingesetzt werden

Um ein Objekt in einer gleichmäßigen Geschwindigkeit bewegen zu können, muss dt wie oben beschrieben berücksichtigt werden. Beispiel: die Sprite-Aktion Move forward ("Bewege dich nach vorne") benötigt einige Pixel, um das Objekt vorwärts zu bewegen. Um das Objekt konstant 60 Pixel pro Sekunde zu bewegen, könnte man es um 60[]dt* Pixel bewegen.

Verhalten (Behaviors) nutzen sowieso schon

dt

Alle Verhalten von Construct 2 nutzen dt in ihren internen Berechnungen. Das bedeutet, dass jedes Objekt, welches durch Verhalten wie Platform und 8 Direction bewegt wird, schon automatisch die Delta-Zeit unterstützt!

Allerdings ist Physics eine Ausnahme. Standardmäßig nutzt es nicht dt und ist demzufolge bildfrequenzabhängig. Das ist weil dt typischerweise kleine Zeitabweichungen besitzt. Diese Abweichungen in einem Physikspiel führen zu unterschiedlichem Verhalten, wenn etwas beispielweise zwei Mal in gleicher Form gemacht wird. Das könnte störend in Physikspielen sein, deswegen ist Physics standardmäßig bildfrequenzabhängig. Dennoch kann man die Nutzung von dt aktivieren, indem man die Physikaktion Set Stepping Mode beim Start des layouts (On start of layout) und man wähle framerate independent, also bildfrequenzunabhängigkeit. Man beachte, dass der maximale Zeitschritt in Physics 1/30 (ca. 33 ms) beträgt. Große Zeitsprünge können in Physiksimulationen zu Instabilität führen.

Zeitskalierungen (Timescaling)

Ein sehr cooles Construct 2 Feature ist timescaling. Es erlaubt einen Wechsel des Grades, wie schnell ein Spiel ausgeführt wird. Die Zeitskalierung oder time scale kann man mit der System-Aktion Set Time Scale festlegen. 1 bedeutet normale Geschwindigkeit, während 0.5 die halbe und 2.0 die doppelte Geschwindigkeit bedeuten. Setzt man time scale auf 0.1, läuft das Spiel um einen Zehntel langsamer - ein schöner und weicher Slo-Mo Effekt!

Die Zeitskalierung verändert den dt Wert. Das heißt, dass Verhalten und damit jedwede dt Bewegung verändert wird. Wird dt in einem Bewegungsalgorithmus nicht benutzt (wie im o.g. ersten Event), wird auch die Bewegung von der Zeitskalierung nicht beeinflusst! Um die Zeitskalierung also zu benutzen, sollte man in jedem Bewegungsalgorithmus dt einsetzen.

Pausieren

Setzt man time scale auf 0, wird jede Bewegung gestoppt. Damit kann man ein Spiel pausieren. Wird time scale auf 1 gesetzt, kann man weiterspielen.

Trotzdem wird man bemerken, dass Eingaben (Maus, Tastatur) weiterhin ausgewertet werden (Schießen während der Pause). Hier empfiehlt es sich, die Spielevents in eine Gruppe zu packen und diese beim Aufrufen der Pausenfunktion zu deaktivieren. Will man weiter spielen, werden die Eingaben wieder aktiviert.

Das Pausieren ist auch eine gute Methode, um zu prüfen, ob man dt richtig einsetzt. Setzt man die Zeitskalierung time scale auf 0, sollte alles im Spiel stoppen. Hat man dt nicht richtig eingesetzt, könnten sich weitere Objekte trotz Pause bewegen! Nun kann man den Bewegungsalgorithmus der Objekte prüfen und gegebenenfalls dt hinzufügen oder korrigieren.

Andere Bewegungsarten

Es ist wichtig zu verstehen, dass bei allen Bewegungsfunktionen dt genutzt werden sollte. Das gilt auch für Rotationen (Drehungen) und Beschleunigung (Acceleration).

Drehungen (Rotation)

Ähnlich wie vorhin, der folgende Event dreht den Piggy-Sprite um 1 Grad bei jedem Tick:

Der Piggy-Sprite dreht sich um 30 Grad bei 30 fps und 60 Grad bei 60 fps. Also unterschiedliche Geschwindigkeiten bei unterschiedlichen Bildfrequenzen. dt löst das Problem - im Folgenden wird der Piggy-Sprite um 60 Grad pro Sekunde drehen - unabhängig von der Bildfrequenz:

[Drehung mit Bildfrequenz-Unabhängigkeit] [5]

Beschleunigung

Beschleunigen ist unkompliziert. Typischerweise geschieht dies, wenn man eine Bewegung durch Events definiert (custom movement).

Hat man eine Variable speed (die eine Objektgeschwindigkeit repräsentiert), wird das Objekt um Objekt.speed [] dt Pixels pro Tick bewegt. Die Objektvariable speed* enthält demnach eine Geschwindigkeitsangabe in Pixel pro Sekunde.

Angenommen, man möchte das Objekt um 100 Pixel pro Sek (im Quadrat) beschleunigen. man muss lediglich +100 [] dt zur Objektvariable speed bei jedem Tick hinzuaddieren. So wird ds Objekt unabhängig von der Bildfrequenz beschleunigt. In anderen Worten, wird dt genutzt, um sowohl die Objektposition als auch die Objektgeschwindigkeit anzupassen.

Häufige Fehler

Man sollte niemals dt in der Bedingung (condition) Every X seconds (also alle x Sekunden)! Ein Event wie Every 1 second (bei jeder Sekunde…) wird immer eine Sekunde laufen - unabhängig von der Bildfrequenz, ist also schon bildfrequenz-unabhängig. Die Bedingung misst Zeit und nicht frames. Definiert man in einem Event Every 60 [] dt seconds (also jede dt Sekunden), hat man irrtümlich die Bedingung nun abhängig der Bildfrequenz gemacht - also das Gegenteil, was man eigentlich vorhatte! Solch eine Bedingung wird jede 6 Sekunden bei 10 fps (wenn z.B: dt=0.1), oder jede 0.6 Sekunden bei 100 fps (wenn dt=0.01) laufen; gibt man einfach Every 6 seconds (alle 6 Sekunden) ein, wird es ungeachtet der Bildfrequenz genau 6 Sekunden laufen.

Fortgeschrittene Überlegungen

Minimale Bildfrequenz

Bei sehr niedrigen Bildfrequenzen kann dt riesig werden. Bei 5 fps beträgt dt 0.2. Ein Objekt, das sich 500 Pixel pro Sekunde bewegt, bewegt sich lediglich mit 100 Pixeln pro Tick. Dadurch kann ein Objekt schlimmstenfalls durch Wände gehen oder Kollisionen finden einfach nicht statt.

Spiele sind bei solch niedrigen Bildfreqnuezen unspielbar und noch schlimmer wird es, wenn die beschriebene Instabilität dazukommt. Um diese Probleme zu verhinden, wird Construct 2 den dt Wert bei maximal 0.1 beschränken. In anderen Worten, unter 10 fps wird dt immer 0.1 betragen. Das bedeutet auch, dass unter 10 fps das Spiel sich in einem Slo-Mo Effekt Modus befinden wird, wie weiter oben beschrieben. Dennoch verhindert man das Problem mit den Nicht-Kollisionen.

Zufällige Abweichungen

Wie weiter oben in Physiksimulationen beschrieben, hat dt sehr kleine und zufallsbedingte Abweichungen - was wohl in den ungenauen Timer des Rechners liegt. Diese Abweichungen sind so gering, dass man dies kaum wahrnehmen wird. Es ist empfehlenswert, immer dt in Spielen zu nutzen, es sei denn, hohe Präzision ist erforderlich.

Zeitskalierungen von Objekten

Es ist möglich, für einzelne Objekte eine eigene Zeitskalierung mit der System-Aktion Set object time scale festzulegen. Damit ist es beispielsweise möglich, das Spiel in einer Slow-Motion Bewegung (Zeitskalierung 0.3) abzuspielen, während der Spieler sich normal bewegt (Zeitskalierung 1). Dies wird dadurch erreicht, dass man den time scale des Spiels auf 0.3 setzt und die Zeitskalierung des Spielers über Set object time scale auf 1 setzt. Der Systemausdruck dt wird nur durch die Zeitskalierung des Spiels beeinflusst. Bei Objekten werden objekteigene dt Ausdrücke (z.B. Player.dt) angesprochen. Also gibt es zwei unterschiedliche dt Werte: die des Spiels und die des Spielers (bzw. streng genommen des Objekts). Dadurch dass sie unterschiedliche Werte an das Spiel zurückgeben, können verschiedene Spielteile in unterschiedlichen Geschwindigkeiten abgespielt werden.

Um in diesem Beispiel den Spieler zurück auf die Bildfrequenz des Spiels zu bringen, bedient man sich der System-Aktion Restore object time scale.

Fazit

Es ist wichtig, das Spiel von Anfang an mit dt zu programmieren. Dies verbessert den Spielfluss und gewährleistet ein gleichmäßiges Spiel ohne Ruckeln in besonders kritischen Spielsequenzen, wo viel los ist. Als Bonus kann man das Feature time scaling nutzen, um ein Spiel zu pausieren oder um die Bildfrequenzen einzelner Objekte zu kontrollieren.

Man sollte nicht vergessen, dass Verhalten (behaviors) - außer physics - immer mit dt arbeiten. Werden in einem Spiel immer Objektverhalten genutzt, braucht man sich keine Gedanken um dt zu machen! Da aber viele Spiele einige Event-abhängige Bewegungen enthalten, sollte man sich stets an die Bildfrequenz-Abhängigkeit erinnern und ggf. anpassen.

  • 0 Comments

  • Order by
Want to leave a comment? Login or Register an account!