Zeit für einen Schlaufendurchgang ermitteln

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
starcow
Establishment
Beiträge: 523
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Zeit für einen Schlaufendurchgang ermitteln

Beitrag von starcow »

Abend ZFX'ler :)

Ich habe in meinem Programm-Code das Problem, dass ich gerne die Zeit für einen Schlaufendruchgang ermitteln würde, um das Spieletempo unabhängig von der CPU-Geschwindigkeit zu halten.
Ich mache das derzeit mittels SDL_GetTicks().
Das Problem ist allerdings, dass die Schleifendurchläufe zwischen 0 und 1 milli Sekunde schwanken. Ist das Time-Delta = 0, bewegt sich natürlich mein Player überhaupt nicht mehr.

Gibt es eine Möglichkeit, die Zeit genauer zu bestimmen? Vielleicht auf die mikro Sekunde genau (ich nutze Windows 7)?

Gruss starcow
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Biolunar
Establishment
Beiträge: 154
Registriert: 27.06.2005, 17:42
Alter Benutzername: dLoB

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von Biolunar »

Sehr empfehlenswerter Artikel dazu: https://gafferongames.com/post/fix_your_timestep/

Unter Windows verwendet man üblicherweise QueryPerformanceCounter für maximale Präzision.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von Krishty »

Ich schließe mich an – mach nicht die Zeitschritte variabel, sondern halt sie konstant und iterier halt unterschiedlich oft.

Und genau, QueryPerformanceCounter().
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von Schrompf »

Nö, std::chrono::high_resolution_clock. Ist QueryPerformanceCounter unter Windows, irgendwas mit zumindest Mikrosekunden-Auflösung unter Linux. Ich habe immer meinen Code mit variablem Timestep berechnet. Läuft aber in Präzissionsprobleme, wenn die Rechner nur gut genug sind. Irgendwann war dann eine Bewegung zu klein und die Objekte blieben auf der Stelle hängen, wenn zu wenig auf dem Bildschirm los war. Fixed Timestep ist zwar mühsam, aber ergibt dafür verlässliche Ergebnisse auch auf Rechnern in 20 Jahren.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von Krishty »

Schrompf hat geschrieben:Nö, std::chrono::high_resolution_clock.
Ach ja, da war was! :D
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
starcow
Establishment
Beiträge: 523
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von starcow »

Vielen Dank für die wertvollen Hinweise! :-)
Ein Grund wieso ich zfx so liebe: Man stellt eine einfache Frage und kriegt gleich noch Hinweise zu Methoden, die wesentlich "fortschrittlicher" sind. Vielen Dank! :mrgreen:

Ich musste mich jetzt erst einmal in die Thematik einarbeiten.
Natürlich sind wiedermal viele Fragen aufgetaucht. Um euch aber nicht endlos damit zu löchern, versuche ich es mal aufs Wesentliche runter zubrechen:

Das der Autor des Artikels ein oberes Limit für das TimeDelta setzt, leuchtet mir ein. Somit ist ausgeschlossen, in eine "Spirale des Todes" zu geraten.
Bei mir sieht diese Massnahme folgendermassen aus:

Code: Alles auswählen

accumulator = min(accumulator, static_cast<unsigned int>(200));
Nach meine Überlegungen verlangsamt sich beim Überschreiten der 200ms Marke das Spiel einfach - und wäre somit nicht mehr "Echtzeit".
Das finde ich jedoch eine gute Lösung, da die ganze Simulation somit nicht in eine Ruckelorgie ausartet.
Zudem müsste jetzt eigentlich mit folgendem Ansatz ausgeschlossen sein, dass das Spiel auf einem sehr schnellen Rechner einfach stehen bleib:

Code: Alles auswählen

timemark = SDL_getTick();
while(running)
timedelta = SDL_getTick() - timemark;
timemark += timedelta;
accumulator += timedelta;
accumulator = min(accumulator, static_cast<unsigned int>(200));
Auch wenn die Schlaufe 100 mal durchläuft und das TimeDelta aufgrund der extrem kleinen Zeitdifferenzen 0 bleibt - bei irgendeinem Schleifendruchgang muss sich eine Zeitdifferenz zeigen. Somit müsste das Problem der stehenden Objekte eigentlich gelöst sein - sofern ich jetzt nicht ein Überlegungsfehler gemacht habe. :mrgreen:

Was ich jedoch nicht ganz verstehe:
Wieso ist es wichtig in der Simulation immer das gleiche Zeitdelta zu nehmen?
Der Ansatz, das Zeitdelta in gleich grosse Häppchen zu unterteilen, finde ich zwar verständlich - doch bei Restgrössen muss dann anscheinend Interpoliert werden, damit keine Ruckler (?) entstehen, durch die mehrmaligen Interationen (sofern ich diesen Punkt jetzt richtig verstanden habe).
Reicht es nicht aus, ein oberes und allenfalls ein unteres Zeitlimit zu setzen?
Gibt man da nicht eine wünschbare Flexibilität auf? Wenn man die Simulationsfunktion einmal mit z. B. 20ms und ein andermal mit z. B. 24ms druchlaufen lassen kann, müsste das doch ein Vorteil sein, da so keine Restgrössen bestehen bleiben?

Und die vorerst letzte Frage:
Irgendwie übersteigt diese std::chrono::high_resolution_clock meine jetzigen Fähigkeiten weil ich mit diesen Template Geschichten nicht auskenne.
Genauer gesagt verstehe ich folgenden Syntax nicht (aus einem Beispiel von cplusplus.com)

Code: Alles auswählen

  high_resolution_clock::time_point t1 = high_resolution_clock::now();

  std::cout << "printing out 1000 stars...\n";
  for (int i=0; i<1000; ++i) std::cout << "*";
  std::cout << std::endl;

  high_resolution_clock::time_point t2 = high_resolution_clock::now();

  duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
Was ist den nun mein Datentyp von time_span? Resp. wie muss ich time_span zuvor deklarieren? Einfach als double? Müsste der Datentyp nicht einfach time_point sein?
Und was genau bewirkt dieses duration<>? Ist das eine Funktion? (Sorry, die Frage hört sich vielleicht etwas dumm an :-/)
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von Krishty »

starcow hat geschrieben:Der Ansatz, das Zeitdelta in gleich grosse Häppchen zu unterteilen, finde ich zwar verständlich - doch bei Restgrössen muss dann anscheinend Interpoliert werden, damit keine Ruckler (?) entstehen, durch die mehrmaligen Interationen (sofern ich diesen Punkt jetzt richtig verstanden habe).
Reicht es nicht aus, ein oberes und allenfalls ein unteres Zeitlimit zu setzen?
Gibt man da nicht eine wünschbare Flexibilität auf? Wenn man die Simulationsfunktion einmal mit z. B. 20ms und ein andermal mit z. B. 24ms druchlaufen lassen kann, müsste das doch ein Vorteil sein, da so keine Restgrössen bestehen bleiben?
Ich bin da leider selber noch nicht zur Wahrheit™ durchgedrungen, aber: Du wählst ein Delta, das möglichst glatt durch andere Zahlen teilbar ist. Z.B. 120. Auf Rechnern mit 60-Hz-Bildschirm verarbeitest du dann glatt 2 Physik-Iterationen pro Grafik-Frame. Auf Rechnern mit 80-Hz-Bildschirm sind es immer abwechselnd 1 und 2, das ruckelt nicht merklich.

Je höher du die Zahl wählst, desto glatter der Ablauf, aber desto mehr CPU-Leistung frisst du logischerweise. Mein Flugsimulator läuft gerade intern auf 240 Hz, und höher schafft mein Rechner nicht mehr. Ich würde liebend gern auf 60 runtergehen, aber dann brauche ich entweder gute Interpolation oder muss feiner granulieren, so dass z.B. die Kollisionsabfrage weiter mit 240 läuft. Ich denke auch über hierarchische Iterationen nach (360 Hz wenn viel kollidiert und durcheinanderfliegt, sonst 120 oder 60, wenn nix los ist). Ist halt nicht einfach.

Weil ich’s interessant fand, eine Liste von Physik-Raten in Fahrsimulatoren: https://www.lfs.net/forum/thread/48927- ... gine-rates
Du siehst, dass viele mit 60 fps auskommen (oder auskamen? War ja vor der VR-Ära), dass es aber auch gern bis 1000 Hz raufgeht.
It was 300Hz originally and somewhere in the middle I changed it to 250Hz in one of the updates around V2 or V3. The 30,000Hz was only very briefly many years ago (well before Virtual RC Racing was ever released; possibly before I even started work on it) and that was only for the tire model.

Anyway, it's 250Hz now with the tire model running at 1000Hz.
Du siehst – keiner hat ’nen Plan. 60, 300, 30.000 … :D
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
starcow
Establishment
Beiträge: 523
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von starcow »

Sehr interessant! Vielen Dank!
Worin liegt denn der Vorteil von festen Zeitdeltas gegeüber flexiblen Werten?

Gruss starcow
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von Krishty »

Darin, dass es reproduzierbar ist.

Wenn du das Spiel startest, und losläufst, und gegen eine Wand läufst, und das Spiel abstürzt – dann kriegst du das mit flexiblen Werten nie wieder reproduziert, weil bei jeder neuen Ausführung andere Zeitdeltas genommen werden und dadurch deine Physik immer unterschiedliche Werte liefert. Mit festen Werten reproduziert es ganz exakt.

Mit flexiblen Zeitdeltas kriegst du Probleme, dass Spieler mit hoher Frame-Rate höher springen oder schneller rennen oder durch Wände laufen können. Mit festen Werten verhält sich die Physik überall exakt gleich.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von Schrompf »

Oder bei den Splitterwelten damals: wenn die Framerate zu hoch wurde, wurde die Physik-Schrittweite (also die Positionsdifferenz) zu klein und die Fließkomma-Mathematik hat die Addition komplett verschluckt. Man konnte sich also gar nicht mehr bewegen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
starcow
Establishment
Beiträge: 523
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von starcow »

Ok, ich danke euch. Das ist gut zu wissen!
Krishty hat geschrieben:Mit flexiblen Zeitdeltas kriegst du Probleme, dass Spieler mit hoher Frame-Rate höher springen oder schneller rennen oder durch Wände laufen können. Mit festen Werten verhält sich die Physik überall exakt gleich.
Der Punkt mit den "durchlaufbaren" Wände leuchtet mir ein. Darum habe ich eigentlich geplant, für die Kollisionsabfrage nicht blos die position heran zuziehen, sondern den tatsächlich zurückgelegten Weg (im geometrischen Sinn).
Weshalb aber soll ein Player mit mehr fps höher springen, wenn ich mit flexiblen Zeitdeltas arbeite? Dieses Verhalten müsste doch analog zur Laufgeschwindigkeit sein.

Gruss starcow
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von Krishty »

Wenn du springst, bewegst du dich ja idealerweise entlang einer Parabel. Theoretisch kannst du dafür eine Gleichung aufstellen, und diese Gleichung für jeden Zeitpunkt lösen, und hast immer ein exaktes Ergebnis.

Praktisch wirken aber außer dem Sprung noch andere Kräfte auf den Spieler – er bewegt sich vorwärts. Er trifft eine Wand. Der Boden war nicht ganz eben, darum schlittert er. Dafür kannst du keine riesen Gleichung bauen.

Also fängst du an, Kraftvektoren zu addieren. Nun ist deine Sprunglösung aber nicht mehr exakt: Du berechnest die Kraft einmal beim Absprung. Dann im nächsten Zeitdelta. Dazwischen nimmst du allerdings einen linearen Zusammenhang an. Und nun hast du nur noch eine Annäherung. (Steht auch so in der Artikelserie.)

Wie jede Annäherung wird sie umso genauer, je kleiner die Iterationen werden (also je höher die Framerate ist). Aber genau oder nicht – sie verändert sich. Höher oder niedriger, beides möglich. Und dann kommen noch Gleitkomma-Besonderheiten dazu.

Du kannst natürlich deine Annäherungen verbessern bis sonstwo und drumherumprogrammieren und und und. Letzendlich hat aber jede Engine von Quake 1 bis aktuellem Unity solche Probleme (das weiß jeder, der Speedruns macht) – obwohl die von Leuten geschrieben wurden, die sich viel besser auskennen als wir. Es ist einfach ein konzeptionelles Problem.

So lange deine Physik in Iterationen arbeitet, ist sie ungenau. Und die Schrittgröße (heißt: Zeiteinheit) verändert das Ergebnis in die eine oder andere Richtung. Ist einfach so.

Und du kannst es eben nur unter Kontrolle kriegen, indem alle Spieler exakt die gleichen Schritte machen (feste Schrittgröße). Exakt ist es dadurch zwar nicht, aber zumindest fair. Dann hat niemand Vor- oder Nachteile durch schnelle oder langsame Rechner.

(Dieser Post wird dir präsentiert von … Grand Theft Auto: Vice City. Auf modernen PCs können die Autos nämlich nicht mehr rückwärts fahren – siehe Schrompfs Kommentar.)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
starcow
Establishment
Beiträge: 523
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von starcow »

Sehr gut erklärt, vielen Dank Krishty! :D

Wenn ich dich richtig verstehe, arbeitet sowohl die Quake-Engine als auch die Unity-Engine mit variablen Zeitdeltas. Deshalb auch der Umstand, das man mit mehr fps weiter springen kann.
Weshalb hatten denn diese Entwickler bei ihren Engines nicht auf konstante Zeitdeltas gesetzt? War das ihnen zu wenig bewusst?
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von Krishty »

starcow hat geschrieben:Wenn ich dich richtig verstehe, arbeitet sowohl die Quake-Engine als auch die Unity-Engine mit variablen Zeitdeltas. Deshalb auch der Umstand, das man mit mehr fps weiter springen kann.
Weshalb hatten denn diese Entwickler bei ihren Engines nicht auf konstante Zeitdeltas gesetzt? War das ihnen zu wenig bewusst?
Keine Ahnung, da stecke ich zu wenig in der Entwicklungsgeschichte drin.

Hier bei 8:55 siehst du das übrigens live in Aktion:
[youtube]0jzzYOkvatI[/youtube]
Wenn die Frame-Rate niedrig ist, laufen die Animationen im Verhältnis schneller ab und die Wache öffnet schneller die Tür. Bis 9:20 erklärt er, was sonst noch so von der Frame Rate abhängt.


(Dieser Post wird dir präsentiert von … Total Air War. Wenn man dort in großen Höhen den Zeitraffer aktiviert, verbraucht das Flugzeug kein Benzin mehr, weil 20.000 l - 160 l/s · 0,0001 s == 20.000 l [Floating-Point Cancellation].)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von Schrompf »

Müsste das nicht genau andersrum sein? Zeitraffer erhöht doch das Delta-T. Und damit müsste die Fließkommaarithmetik bei Zeitraffer also besser funktionieren als bei Normalzeit.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
mrz
Beiträge: 79
Registriert: 07.08.2008, 14:34

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von mrz »

starcow hat geschrieben: Wenn ich dich richtig verstehe, arbeitet sowohl die Quake-Engine als auch die Unity-Engine mit variablen Zeitdeltas. Deshalb auch der Umstand, das man mit mehr fps weiter springen kann.
Weshalb hatten denn diese Entwickler bei ihren Engines nicht auf konstante Zeitdeltas gesetzt? War das ihnen zu wenig bewusst?
Im Falle von Quake 3 was das anfäglich tatsächlich so. Sehr gut liess sich das jeweils in q3dm13 prüfen ob man das Megahealth erreichen konnte.
Wenn das der Fall was dann war alles gut :-)
Später wurde es gefixt [1] und man konnte es auch mit der Einstellung "pmove_fixed" konfigurieren.
Hier gibt es noch eine ausführliche Erklärung:
https://www.youtube.com/watch?v=he02vJvKaRs

Als ehemals langjähriger Q3 Defrag Spieler möchte ich drauf hinweisen dass die Physics von Q3 einige interessante Aspekte hat.
Die meisten kennen nur den klassischen Strafe Jump. Weniger bekannt ist der Circle Jump oder Micro Strafe Jumps [2] (sieht einfach aus, ist es aber nicht).
Noch unbekannter ist ein "Bug" welcher "Overbounce" genannt wird wobei es den in "Vertical", "Horizontal" und "Diagonal" [3] gibt,
letzterer ist mit Abstand am schwierigsten. Mit dem Q3 Defrag Mod besteht die Möglichkeit sich im HUD diverse Hilfsstellungen
und Indikatoren anzuzeigen (um optimale Strafe Jumps zu trainieren oder z.B. Bounces zu "finden").
Hier ein Video wo man so ein HUD sieht und welches den Vertical und Horizontal Overbounce zeigt:
https://www.youtube.com/watch?v=5DZIkZBTh94

Zum Abschluss noch ein Vergleich zweier unterschiedliche Ansätze bei der selben Map warum Defrag einfach toll ist:
https://www.youtube.com/watch?v=OJuIlFtiD7k
https://www.youtube.com/watch?v=syF_Wp_-GKs


[1] https://www.eurogamer.net/articles/a_q3-125
[2] https://www.youtube.com/watch?v=0teArqIPgQY
[3] https://www.youtube.com/watch?v=B3QA4unCHhw
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von Krishty »

Schrompf hat geschrieben:Müsste das nicht genau andersrum sein? Zeitraffer erhöht doch das Delta-T. Und damit müsste die Fließkommaarithmetik bei Zeitraffer also besser funktionieren als bei Normalzeit.
Heiliger Halbleiter, du hast recht! Und ich finde den verdammten Beitrag nicht mehr, in dem’s erklärt war :(
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
starcow
Establishment
Beiträge: 523
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: Zeit für einen Schlaufendurchgang ermitteln

Beitrag von starcow »

Krishty hat geschrieben: Heiliger Halbleiter, du hast recht!
Sehr gut! Ein Fan der alten Batman-Serie? :mrgreen:
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Antworten