Sollte eine Spieleschleife auf festen oder variablen Zeitschritten basieren? Ist man immer überlegen oder variiert die richtige Wahl je nach Spiel?
Variabler Zeitschritt
Physik-Aktualisierungen werden als Argument "Seit der letzten Aktualisierung verstrichene Zeit" übergeben und sind daher von der Framerate abhängig. Dies kann bedeuten, Berechnungen durchzuführen als position += distancePerSecond * timeElapsed
.
Vorteile : glatt, einfacher zu codieren
Nachteile : nicht deterministisch, bei sehr kleinen oder großen Schritten unvorhersehbar
deWiTTERS Beispiel:
while( game_is_running ) {
prev_frame_tick = curr_frame_tick;
curr_frame_tick = GetTickCount();
update( curr_frame_tick - prev_frame_tick );
render();
}
Fester Zeitschritt
Aktualisierungen akzeptieren möglicherweise nicht einmal eine "verstrichene Zeit", da davon ausgegangen wird, dass jede Aktualisierung für einen festgelegten Zeitraum durchgeführt wird. Berechnungen können wie folgt durchgeführt werden position += distancePerUpdate
. Das Beispiel enthält eine Interpolation während des Renderns.
Vorteile : vorhersehbar, deterministisch (einfacher zu vernetzen, zu synchronisieren?), Klarerer Berechnungscode
Nachteile : nicht synchronisiert, um die V-Synchronisation zu überwachen (verursacht unruhige Grafiken, sofern Sie nicht interpolieren), begrenzte maximale Bildrate (sofern Sie nicht interpolieren) variable Zeitschritte annehmen (wie Pyglet oder Flixel )
deWiTTERS Beispiel:
while( game_is_running ) {
while( GetTickCount() > next_game_tick ) {
update();
next_game_tick += SKIP_TICKS;
}
interpolation = float( GetTickCount() + SKIP_TICKS - next_game_tick )
/ float( SKIP_TICKS );
render( interpolation );
}
Einige Ressourcen
- Oberbeleuchter bei Spielen: Korrigieren Sie Ihren Zeitschritt!
- deWitters Game-Loop-Artikel
- Die FPS von Quake 3 beeinflussen die Sprungphysik - ein Grund, warum Doom 3 auf 60 fps festgelegt ist?
- Flixel erfordert einen variablen Zeitschritt (ich denke, dies wird von Flash bestimmt), während Flashpunk beide Typen zulässt.
- Box2Ds Handbuch § Die Simulation der Welt von Box2D schlägt vor, konstante Zeitschritte zu verwenden.
quelle
Antworten:
Es gibt zwei Probleme im Zusammenhang mit der Frage.
In Glen Fielder's Fix your time step sagt er zu "Free the Physics". Das bedeutet, dass die Aktualisierungsrate Ihrer Physik nicht an Ihre Bildrate gebunden sein sollte .
In Erin Cattos Empfehlungen für Box2D befürwortet er dies ebenfalls.
Sollte die Physik-Schrittfrequenz an Ihre Bildfrequenz gebunden sein? Nein.
Erins Gedanken zu Fixed Step vs. Variable Stepping:
Glen's Gedanken zu festen und variablen Schritten:
Sollte die Physik mit konstanten Deltas versehen werden? Ja.
Der Weg, die Physik mit konstanten Deltas zu verbessern und die Aktualisierungsrate der Physik nicht an die Framerate zu binden, besteht darin, einen Zeitakkumulator zu verwenden. In meinem Spiel gehe ich noch einen Schritt weiter. Ich wende eine Glättungsfunktion auf die eingehende Zeit an. Auf diese Weise bewirken große FPS-Spikes, dass die Physik nicht zu weit springt, sondern dass sie für ein oder zwei Frames schneller simuliert werden.
Sie erwähnen, dass mit einer festen Rate die Physik nicht mit dem Display synchronisiert werden würde. Dies gilt, wenn die Zielphysikrate in der Nähe der Zielbildrate liegt. Es ist schlimmer, die Bildrate ist größer als die Physikrate. Im Allgemeinen ist es besser, eine Physik-Aktualisierungsrate zu erreichen, die doppelt so hoch ist wie die Ziel-FPS, wenn Sie es sich leisten können.
Wenn Sie sich keine hohe Aktualisierungsrate für die Physik leisten können, sollten Sie die Positionen der Grafiken zwischen den Frames interpolieren, damit die gezeichneten Grafiken sich reibungsloser bewegen, als sich die Physik tatsächlich bewegt.
quelle
Ich denke, es gibt wirklich 3 Optionen, aber Sie listen sie als nur 2 auf:
Option 1
Nichts tun. Versuchen Sie, in einem bestimmten Intervall zu aktualisieren und zu rendern, z. B. 60 Mal pro Sekunde. Wenn es zurückfällt, lass es und mach dir keine Sorgen. Die Spiele verlangsamen sich in ruckelige Zeitlupe, wenn die CPU nicht mit Ihrem Spiel Schritt halten kann. Diese Option funktioniert überhaupt nicht für Echtzeitspiele mit mehreren Benutzern, ist jedoch für Einzelspielerspiele geeignet und wurde in vielen Spielen erfolgreich verwendet.
Option 2
Verwenden Sie die Deltazeit zwischen den Aktualisierungen, um die Bewegung von Objekten zu variieren. In der Theorie großartig, besonders wenn nichts in Ihrem Spiel beschleunigt oder verlangsamt, sondern sich nur mit einer konstanten Geschwindigkeit bewegt. In der Praxis wird dies von vielen Entwicklern schlecht umgesetzt, was zu inkonsistenter Kollisionserkennung und -physik führen kann. Es scheint, dass einige Entwickler diese Methode für einfacher halten als sie ist. Wenn Sie diese Option nutzen möchten, müssen Sie Ihr Spiel erheblich verbessern und einige hochkarätige Berechnungen und Algorithmen herausbringen, z. B. mithilfe eines Verlet-Physik-Integrators (anstelle des Standard-Eulers, den die meisten Leute verwenden) und mithilfe von Strahlen zur Kollisionserkennung eher als einfache Pythagoras-Entfernungsprüfungen. Ich habe vor einiger Zeit bei Stack Overflow eine Frage dazu gestellt und einige großartige Antworten erhalten:
https://stackoverflow.com/questions/153507/calculate-the-position-of-an-accelerating-body-after-a-certain-time
Option 3
Verwenden Sie den Gaffer-Ansatz "Fix your time step". Aktualisieren Sie das Spiel in festen Schritten wie in Option 1, aber tun Sie dies mehrmals pro gerendertem Frame - je nachdem, wie viel Zeit vergangen ist -, damit die Spiellogik mit der Echtzeit Schritt hält und gleichzeitig in diskreten Schritten bleibt. Auf diese Weise funktionieren einfach zu implementierende Spielelogik wie Euler-Integratoren und einfache Kollisionserkennung immer noch. Sie haben auch die Möglichkeit, grafische Animationen basierend auf der Deltazeit zu interpolieren. Dies gilt jedoch nur für visuelle Effekte und hat keine Auswirkungen auf Ihre Kernlogik. Sie können möglicherweise in Schwierigkeiten geraten, wenn Ihre Aktualisierungen sehr intensiv sind. Wenn die Aktualisierungen zurückbleiben, benötigen Sie immer mehr davon, um auf dem Laufenden zu bleiben. Dadurch wird Ihr Spiel möglicherweise noch weniger reaktionsschnell.
Persönlich mag ich Option 1, wenn ich damit durchkomme, und Option 3, wenn ich mit Echtzeit synchronisieren muss. Ich respektiere, dass Option 2 eine gute Option sein kann, wenn Sie wissen, was Sie tun, aber ich kenne meine Grenzen gut genug, um mich davon fernzuhalten.
quelle
Mir gefällt die Art und Weise, wie das XNA Framework feste Zeitschritte implementiert. Wenn ein bestimmter Draw-Aufruf etwas zu lange dauert, wird update so oft aufgerufen, bis er "aufholt". Shawn Hargreaves beschreibt es hier:
http://blogs.msdn.com/b/shawnhar/archive/2007/11/23/game-timing-in-xna-game-studio-2-0.aspx
Meiner Meinung nach haben Sie einen der größten Profis erwähnt, der all Ihre Spielcode-Berechnungen so viel einfacher macht, weil Sie diese Zeitvariable nicht überall einbeziehen müssen.
Hinweis: xna unterstützt auch variable Zeitschritte. Dies ist nur eine Einstellung.
quelle
Es gibt noch eine andere Option - Spiele-Update und Physik-Update entkoppeln. Der Versuch, die Physik-Engine auf den Zeitschritt des Spiels abzustimmen, führt zu Problemen, wenn Sie Ihren Zeitschritt korrigieren (das Problem, dass die Integration außer Kontrolle gerät, weil mehr Zeitschritte erforderlich sind, die mehr Zeit benötigen, die mehr Zeitschritte erfordern), oder wenn Sie sie variabel machen und eine wackelige Physik erzielen.
Die Lösung, die ich oft sehe, besteht darin, die Physik in einem festen Zeitschritt in einem anderen Thread (auf einem anderen Kern) laufen zu lassen. Das Spiel interpoliert oder extrapoliert die beiden zuletzt gültigen Frames, die es erfassen kann. Interpolation fügt eine gewisse Verzögerung hinzu, Extrapolation fügt eine gewisse Unsicherheit hinzu, aber Ihre Physik wird stabil sein und Ihren Zeitschritt nicht außer Kontrolle bringen.
Dies ist nicht trivial zu implementieren, könnte sich aber als zukunftssicher erweisen.
quelle
Persönlich verwende ich eine Variation von variablen Zeitschritten (was meiner Meinung nach eine Art Hybrid aus festen und variablen Schritten ist). Ich habe dieses Timing-System auf verschiedene Arten getestet und benutze es für viele Projekte. Empfehle ich es für alles? Wahrscheinlich nicht.
Meine Spielschleifen berechnen die Anzahl der zu aktualisierenden Frames (nennen wir dies F) und führen dann F diskrete Logikaktualisierungen durch. Jedes Logik-Update setzt eine konstante Zeiteinheit voraus (die in meinen Spielen oft eine Hundertstelsekunde beträgt). Jede Aktualisierung wird der Reihe nach durchgeführt, bis alle F Aktualisierungen der diskreten Logik durchgeführt wurden.
Warum diskrete Aktualisierungen in logischen Schritten? Nun, wenn Sie versuchen, kontinuierliche Schritte zu verwenden, und plötzlich Störungen in der Physik auftreten, werden die berechneten Geschwindigkeiten und zurückgelegten Strecken mit einem riesigen Wert von F multipliziert.
Eine schlechte Implementierung würde nur F = aktuelle Zeit - Aktualisierungen der letzten Frame-Zeit bewirken. Wenn die Berechnungen jedoch zu weit zurückliegen (manchmal aufgrund von Umständen, die außerhalb Ihrer Kontrolle liegen, wie z. B. ein anderer Prozess, der die CPU-Zeit stiehlt), werden Sie schnell ein schreckliches Überspringen feststellen. Schnell wird aus dem stabilen FPS, den Sie beibehalten möchten, ein SPF.
In meinem Spiel erlaube ich eine "sanfte" (Art) Verlangsamung, um das Ausmaß des logischen Nachholens zu begrenzen, das zwischen zwei Ziehungen möglich sein sollte. Ich mache dies durch Klemmen von: F = min (F, MAX_FRAME_DELTA), was normalerweise MAX_FRAME_DELTA = 2/100 * s oder 3/100 * s hat. Also, anstatt Frames zu überspringen, wenn sie zu weit hinter der Spielelogik liegen, verwerfen Sie jeden massiven Frame-Verlust (der die Dinge verlangsamt), stellen Sie ein paar Frames wieder her, zeichnen Sie und versuchen Sie es erneut.
Auf diese Weise stelle ich auch sicher, dass die Steuerelemente des Players näher an den tatsächlichen Anzeigen auf dem Bildschirm ausgerichtet sind.
Der Pseudocode des Endprodukts sieht ungefähr so aus (Delta ist F, wie bereits erwähnt):
Diese Art der Aktualisierung ist nicht für alles geeignet, aber für Spiele im Arcade-Stil würde ich es vorziehen, wenn das Spiel langsamer wird, da viel los ist, als Frames zu verpassen und die Kontrolle über den Spieler zu verlieren. Ich bevorzuge dies auch anderen zeitvariablen Schritten, bei denen irreproduzierbare Störungen durch Bildverlust auftreten.
quelle
Diese Lösung gilt nicht für alles, aber es gibt eine andere Ebene variabler Zeitschritte - variable Zeitschritte für jedes Objekt auf der Welt.
Dies scheint kompliziert zu sein, und es kann auch sein, aber denken Sie daran, dass Sie Ihr Spiel als diskrete Ereignissimulation modellieren. Jede Spielerbewegung kann als Ereignis dargestellt werden, das beginnt, wenn die Bewegung beginnt, und endet, wenn die Bewegung endet. Wenn es eine Interaktion gibt, bei der das Ereignis aufgeteilt werden muss (z. B. eine Kollision), wird das Ereignis abgebrochen und ein anderes Ereignis in die Ereigniswarteschlange verschoben (bei der es sich wahrscheinlich um eine Prioritätswarteschlange handelt, die nach Ereignisendzeit sortiert ist).
Das Rendern ist vollständig von der Ereigniswarteschlange getrennt. Die Anzeigemaschine interpoliert Punkte zwischen den Start- / Endzeiten des Ereignisses nach Bedarf und kann bei dieser Schätzung so genau oder so nachlässig sein, wie dies erforderlich ist.
Eine komplexe Implementierung dieses Modells finden Sie im Raumsimulator EXOFLIGHT . Es verwendet ein anderes Ausführungsmodell als die meisten Flugsimulatoren - ein ereignisbasiertes Modell anstelle des herkömmlichen festen Zeitscheibenmodells. Die grundlegende Hauptschleife dieser Art von Simulation sieht im Pseudocode folgendermaßen aus:
Der Hauptgrund für die Verwendung in einem Raumsimulator ist die Notwendigkeit, eine willkürliche Zeitbeschleunigung ohne Genauigkeitsverlust bereitzustellen. Einige Missionen in EXOFLIGHT können Jahre in Anspruch nehmen, und selbst eine 32-fache Beschleunigungsoption wäre unzureichend. Für eine verwendbare Sim ist eine Beschleunigung von über 1.000.000 erforderlich, was in einem Zeitscheibenmodell schwierig ist. Mit dem ereignisbasierten Modell erhalten wir beliebige Zeitraten von 1 s = 7 ms bis 1 s = 1 Jahr.
Das Ändern der Zeitrate ändert nicht das Verhalten der Sim, was ein kritisches Merkmal ist. Wenn nicht genügend CPU-Leistung zur Verfügung steht, um den Simulator mit der gewünschten Geschwindigkeit auszuführen, häufen sich die Ereignisse und wir können die Aktualisierung der Benutzeroberfläche einschränken, bis die Ereigniswarteschlange geleert ist. Ebenso können wir die Sim so schnell vorspulen, wie wir möchten, und sicherstellen, dass wir weder CPU verschwenden noch Genauigkeit einbüßen.
Fazit: Wir können ein Fahrzeug in einer langen, gemächlichen Umlaufbahn modellieren (mithilfe der Runge-Kutta-Integration) und ein anderes Fahrzeug, das gleichzeitig über den Boden hüpft - beide Fahrzeuge werden mit der entsprechenden Genauigkeit simuliert, da wir keinen globalen Zeitschritt haben.
Nachteile: Komplexität und Mangel an handelsüblichen Physik-Engines, die dieses Modell unterstützen :)
quelle
Ein fester Zeitschritt ist nützlich, wenn die Gleitkommagenauigkeit berücksichtigt und Aktualisierungen konsistent gemacht werden sollen.
Da es sich um einen einfachen Code handelt, ist es hilfreich, ihn auszuprobieren und zu prüfen, ob er für Ihr Spiel funktioniert.
Das Hauptproblem bei der Verwendung eines festen Zeitschritts ist, dass Spieler mit einem schnellen Computer die Geschwindigkeit nicht nutzen können. Das Rendern mit 100 fps, wenn das Spiel nur mit 30 fps aktualisiert wird, entspricht dem Rendern mit 30 fps.
Abgesehen davon kann es möglich sein, mehr als einen festen Zeitschritt zu verwenden. Mit 60 fps können triviale Objekte (z. B. Benutzeroberfläche oder animierte Sprites) und mit 30 fps nicht-triviale Systeme (z. B. Physik und) aktualisiert werden. Hinter den Kulissen können auch langsamere Zeitgeber (z. B. Löschen nicht verwendeter Objekte, Ressourcen usw.) verwaltet werden.
quelle
Abgesehen von dem, was Sie bereits angegeben haben, kann es sein, dass Sie das Gefühl haben, Ihr Spiel zu haben. Wenn Sie nicht garantieren können, dass Sie immer eine konstante Bildrate haben, ist es wahrscheinlich, dass Sie irgendwo langsamer werden und feste und variable Zeitschritte sehr unterschiedlich aussehen. Behoben hat den Effekt, dass Ihr Spiel für eine Weile in Zeitlupe läuft, was manchmal der beabsichtigte Effekt sein kann. Variable Zeitschritte sorgen dafür, dass sich die Dinge in Bezug auf die Zeit mit der richtigen Geschwindigkeit bewegen. Es kann jedoch zu plötzlichen Positionsänderungen usw. kommen, die es dem Spieler erschweren können, Aktionen genau auszuführen.
Ich kann nicht wirklich erkennen, dass ein festgelegter Zeitschritt die Dinge in einem Netzwerk einfacher macht. Sie wären zunächst alle leicht nicht synchron und würden auf einer Maschine langsamer, aber keine andere würde die Dinge mehr aus der Synchronität bringen.
Ich habe mich immer persönlich für den variablen Ansatz interessiert, aber diese Artikel enthalten einige interessante Dinge, über die man nachdenken muss. Ich habe jedoch immer noch häufig feste Schritte gefunden, insbesondere auf Konsolen, bei denen die Framerate konstant 60 fps ist, verglichen mit den sehr hohen Raten, die auf dem PC erzielt werden können.
quelle
Verwenden Sie den Gaffer-Ansatz "Fix your time step". Aktualisieren Sie das Spiel in festen Schritten wie in Option 1, aber tun Sie dies mehrmals pro gerendertem Frame - je nachdem, wie viel Zeit vergangen ist -, damit die Spiellogik mit der Echtzeit Schritt hält und gleichzeitig in diskreten Schritten bleibt. Auf diese Weise funktionieren einfach zu implementierende Spielelogik wie Euler-Integratoren und einfache Kollisionserkennung immer noch. Sie haben auch die Möglichkeit, grafische Animationen basierend auf der Deltazeit zu interpolieren. Dies gilt jedoch nur für visuelle Effekte und hat keine Auswirkungen auf Ihre Kernlogik. Sie können möglicherweise in Schwierigkeiten geraten, wenn Ihre Aktualisierungen sehr intensiv sind. Wenn die Aktualisierungen zurückbleiben, benötigen Sie immer mehr davon, um auf dem Laufenden zu bleiben. Dadurch wird Ihr Spiel möglicherweise noch weniger reaktionsschnell.
Persönlich mag ich Option 1, wenn ich damit durchkomme, und Option 3, wenn ich mit Echtzeit synchronisieren muss. Ich respektiere, dass Option 2 eine gute Option sein kann, wenn Sie wissen, was Sie tun, aber ich kenne meine Grenzen gut genug, um mich davon fernzuhalten
quelle
Ich habe festgestellt, dass feste Zeitschritte, die mit 60 fps synchronisiert sind, eine spiegelglatte Animation ergeben. Dies ist besonders wichtig für VR-Anwendungen. Alles andere ist körperlich übel.
Variable Zeitschritte sind für VR nicht geeignet. Schauen Sie sich einige Unity VR-Beispiele an, die variable Zeitschritte verwenden. Es ist unangenehm.
Die Regel ist, wenn Ihr 3D-Spiel im VR-Modus flüssig ist, wird es im Nicht-VR-Modus ausgezeichnet sein.
Vergleichen Sie diese beiden (Cardboard VR-Apps)
(Variable Zeitschritte)
(Feste Zeitschritte)
Ihr Spiel muss Multithreading-fähig sein, um eine konsistente Zeit- / Bildrate zu erzielen. Physik, Benutzeroberfläche und Rendering müssen in dedizierte Threads unterteilt werden. Es ist scheußlich, sie mit PITA zu synchronisieren, aber das Ergebnis ist genau das, was Sie möchten (insbesondere für VR).
Handyspiele sind insb. Eine Herausforderung, da die Leistung der eingebetteten CPUs und GPUs begrenzt ist. Verwenden Sie sparsam GLSL (Slang), um so viel Arbeit wie möglich von der CPU zu entladen. Beachten Sie, dass die Übergabe von Parametern an die GPU Busressourcen beansprucht.
Halten Sie Ihre Bildrate während der Entwicklung immer angezeigt. Das eigentliche Spiel ist, es auf 60 fps zu fixieren. Dies ist die native Synchronisationsrate für die meisten Bildschirme und auch für die meisten Augäpfel.
Das von Ihnen verwendete Framework sollte in der Lage sein, Sie über eine Synchronisierungsanforderung zu benachrichtigen oder einen Timer zu verwenden. Fügen Sie keine Schlaf- / Wartezeit ein, um dies zu erreichen - selbst geringfügige Abweichungen sind erkennbar.
quelle
Variable Zeitschritte sind für Prozeduren, die so oft wie möglich ausgeführt werden sollen: Rendern von Zyklen, Ereignisbehandlung, Netzwerkmaterial usw.
Feste Zeitschritte sind dafür vorgesehen, dass etwas vorhersehbar und stabil sein muss. Dies beinhaltet, ist aber nicht beschränkt auf Physik- und Kollisionserkennung.
In der Praxis sollten Physik und Kollisionserkennung in einem eigenen Zeitschritt von allem anderen entkoppelt werden. Der Grund für die Durchführung solcher Verfahren in einem kleinen festen Zeitschritt besteht darin, sie genau zu halten. Die Impulswerte hängen stark von der Zeit ab, und wenn das Intervall zu groß wird, wird die Simulation instabil, und verrücktes Zeug passiert wie ein Ball, der in Phasen durch den Boden springt oder aus der Spielwelt springt. Beides ist nicht wünschenswert.
Alles andere kann in einem variablen Zeitschritt ausgeführt werden (obwohl es professionell gesehen oft eine gute Idee ist, das Rendern auf einen festen Zeitschritt zu beschränken). Damit eine Game-Engine reagieren kann, sollten Dinge wie Netzwerknachrichten und Benutzereingaben so schnell wie möglich behandelt werden, was bedeutet, dass das Intervall zwischen den Abfragen idealerweise so kurz wie möglich sein sollte. Dies bedeutet im Allgemeinen variabel.
Alles andere kann asynchron gehandhabt werden, was das Timing zu einem Streitpunkt macht.
quelle