Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

idontknow

unregistriert

1

23.09.2018, 16:56

Advanced game loop: Fixed update timestep, variable rendering - flackern im Rendering!

Hi,

ich hab neuerdings einen Artikel zum Thema wie man eine Game Loop vernünftig für verschieden schnelle Systeme implementiert gelesen[0] und bin dabei eine entsprechende Implementierung dafür auszuprobieren[1]. Der tl;dr des Artikels ist, dass die Game-Loop die Zeit zwischen 2 Frames misst und falls notwendig mehrere Game Update Schritte durchführt, die die Welt je einen fixen Zeitschritt simulieren. Das rendering ist davon unabhängig und kann theoretisch so oft es will bzw. so oft das System rendern kann durchgeführt werden, aber bei langsameren Systemen werden entsprechend Rendering-Schritte weggeworfen.
Ein Knackpunkt der Geschichte ist, dass zwischen dem letzten und aktuellen Gamestate interpoliert wird um das ganze etwas zu glätten.
Dabei habe ich aber 1-2 Probleme / Fragen:
In der Implementierung wird zwischen dem vorherigen und aktuellen Zustand basierend auf der Restzeit (dt < timestep) interpoliert, d.h. die ganze Spielsimulation hängt einen Zeitschritt hinterher. Der timestep stellt die Zeit zwischen 2 Frames dar und dt ist ein Bruchteil dieser Zeit entsprechend die Zeit bis zum Folgezustand, also eigentlich dem Spielzustand +1 in der Zukunft! Interpoliert wird aber zwischen GS[t-1] und GS[t0] anstatt zwischen GS[t0] und GS[t+1]. In meiner Recherche wurde das scheinbar öfter einfach so hingenommen, aber ich verstehe da nicht warum ich nicht einfach die Interpolation weglasse und den GS[0] direkt rendern sollte. Wenn ich das in meiner Implementierung tue, dann habe ich auch kein flackern!
Die Alternative wäre ja die Extrapolation, also einfach auf Basis des aktuellen Zustandes die Zukunft also den GS[t0+dt] vorherzusagen. In 1-2 Posts dazu die ich gefunden habe wurde das nicht empfohlen, da dann ein Flackern auftreten würde, wenn sich die Eingabe ändert und nicht mehr mit der Vorhersage überein stimmt.

Bei mir habe ich wie gesagt bei Interpolation alle paar Frames ein flackern. Wirkt fast so als hätte ich Extrapolation gemacht statt Interpolation, aber hab ich leider nicht. In meiner Demo wird lediglich 1 Charakter gerendet, der mit der Tastatur gesteuert werden kann. Also alles super simpel.

Insgesamt wäre bei dem Thema wohl interessant wie das die etwas erfahreneren hier die schon Spiele, die bei mehreren Leuten laufen gelöst haben. Bei Bedarf kann ich den Code hochladen, ist momentan aber alles in eine einziger Datei, da super experimentell.

Noch ein Nachtrag bzw. 2 Teil zu der Geschichte: Das Interpolieren von Gamestates habe ich jetzt bei mir über ein striktes Trennen des Zustands von der Objekt-Logik, d.h. in meiner aktuellen Demo habe ich ein für meinen Helden ein "Controller-Objekt", dass den relevanten Input-Zustand speichert, ein "Helden-Objekt", dass aus dem Input-Zustand entsprechende Schalter für ein Update des Helden-Zustands setzt, ein Helden-Zustand (der Teil des Helden-Objekts ist), der alle relevanten Zustandsdaten hält und am wichtigsten das Update implementiert. Und zum Schluss noch ein "Helden-Renderer-Objekt", aber das ist hierfür nicht relevant. Das ist jetzt zumindest einmal EIN Weg, den Gamestate aus dem Rest heraus zu lösen. Dann kann ich auch relativ angenehm die entsprechenden + und * operatoren überladen um die Interpolation der Gamestates angenehm umsetzen zu können. Das Problem ist, dass das ein Haufen Code drumrum ist, der implementiert werden muss. Und meiner Meinung nach ehrlich gesagt extrem viel! Die Krönung war jetzt, dass ich zum weiter testen eine Klasse für ein aufladbares Attribut (Ausdauer, Leben, ..) implementiert habe von der eine Instanz im Helden-Zustand gespeichert wird. Zum einen brauche ich in der Klasse selber direkt wieder passende Konstruktoren / Operatoren (vor allem eben + und *) und muss anschließend alle Helden-Zustand Operatoren usw. anpassen, also der Rattenschwanz hinter einer solchen Änderung ist enorm.. Eigentlich kann ich mir nur schwer vorstellen, dass das auch nur ansatzweise an einer optimalen Architektur Lösung ist! Hat jemand zum abkapseln des Zustands und eben der Interpolations Implementierung Tipps/Erfahrungen?

Aktuell ist das bei mir alles eine experimentelle Demo um zu testen, wie ich das Konzept mit den interpolierten GameStates in er Praxis vernünftig umsetzen kann, denn in den Artikeln dazu wird immer einfach "State interpolated = lerp(oldstate, newstate, alpha)" geschrieben, was natürlich richtig und einfach ist, aber über den Hintergrund was da passiert gibt es wenig Informationen (bzw. wie man das eben vernünftig umsetzt).

Link zur (experimentellen!!) Demo: https://github.com/ftbmynameis/YetAnotherGame2018
Experimental, daher tendenziell quick and dirty (globals und so).

Gruß,
idk

[0] - http://gameprogrammingpatterns.com/game-loop.html
[1] - https://gist.github.com/mariobadr/673bbd5545242fcf9482

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »idontknow« (23.09.2018, 23:21)


David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

2

23.09.2018, 21:24

Interpolation ist sinnvoll, um auch bei niedrigen Update-Raten flüssige Grafik zu haben. Z. B. könnte die Spiellogik mit 30 Hz laufen, aber rendern tust du mit 120 Hz (neuere Monitore können das oder dank FreeSync/G-Sync sogar noch viel mehr).
Wir haben übrigens auch einen Wiki-Artikel dazu, wo auch der Sinn der Interpolation erklärt wird: https://www.spieleprogrammierer.de/wiki/…gige_Spiellogik

idontknow

unregistriert

3

23.09.2018, 22:41

Ja genau, aber mich würde jetzt vor allem noch interessieren wie man die Architektur bezüglich der gespeicherten Spielzustände vernünftig wählt, sodass die Interpolation möglichst angenehm von der Hand geht.
Ich werd meinen Demo-Code morgen mal aufräumen und auf Github posten, damit man sieht wie ich das bisher gelöst habe und vielleicht besser klar wird wo genau mein Problem liegt.

edit: Ich habe im Original Post einen Link zu dem Demo-Code gepostet. Ich hoffe daraus wird ein bisschen besser ersichtlich, welche Probleme ich habe oder eher was ich irgendwie lösen möchte!
Benötigt wird nur SFML 2.5 mit den entsprechenden Libraries. Die IDE ist VS 2015. Mit WASD kann der Held bewegt werden (in Richtung des Mauszeigers), mit Shift kann er eine begrenzte Zeit sprinten.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »idontknow« (23.09.2018, 23:23)


Werbeanzeige