Ich bin ja eher ein Fan vom Multithreading-Ansatz. Aber wie #NULL schon schrieb, brauch man sich um die Framerate theoretisch nicht kümmern. Praktisch hingegen würde ich das ganze aber vielleicht anders angehen. 2D Spiele sind etwas anders zu handhaben als 3D-Spiele. Bei 3D-Spielen schwanken Framerates teils sehr stark und Animationen haben oftmals eine deutlich höhere Auflösung als bei 2D-Spielen, wodurch es oftmals zu unschönen Effekten kommt und Bewegungen nicht mehr flüssig wiedergegeben werden. Da sollte man mit einem aus der Framerate errechnetem Faktor die Bewegungen und Werte entsprechend glätten, damit einem bei weniger Frames das Gameplay nicht langsamer vorkommt.
Bei 2D-Spielen hingegen steht die Frage im Raum, ob es nicht vielleicht doch Sinn macht, die Framerate festzusetzen. 2D Animationen haben auf Grund der Fülle an Einzelbildern und den heute hohen Auflösungen selten mehr wie 15, im besten Fällen 30 Bilder die Sekunde. Ich habe bisher noch kein 2D Spiel gesehen, das mehr wie 30 Bilder für eine Animation verwendet hat. Das Spritefile wäre irgendwann einfach extrem groß. Da kommt man schnell in den Megapixel-Bereich.
Worauf ich also hinaus will. Es ist vollkommen egal, ob das Spiel nun 30, 60, 120, oder 1596 FPS läuft. Wenn die Animation immer in einer gleichbleibenden Geschwindigkeit abgespielt werden sollen, dann werden über 30 FPS die Bewegungen der Annimationen auch nicht flüssiger. Allerdings ist es ein Krampf, einen guten Algorithmus zu schreiben, der alles bei nicht Konstanten Raten berücksichtigt. Wenn man einen Faktor errechnet, mit dem die Werte an die Framerate angepasst werden, benötigt man erst einmal eine Framerate, an der man sich orientieren kann. Je niedriger diese ist, umso schlimmer wird es. Orientiert man sich an 30 FPS, dann sind niedrigere Werte kein Problem. Schafft das System nur 15 FPS, beträgt der Faktor 2 und die Werte für die Achsen oder die Winkel werden einfach verdoppelt. Bei krummen zahlen würde beim kaufmännischen Runden die Bewegung nur wenig leiden.
Schafft das System allerdings 150 FPS (was bei 2D spielen nicht gerade selten ist), beträgt der Faktor 0,2. Wenn man nun aber eine langsame Bewegung hat (60 Pixel die Sekunde), hat jeder Step bei 30 FPS 2 Pixel. Pro Step wären es also bei 150 FPS 0,4 Pixel. Das geht natürlich nicht (man brauch eine Ganzzahl) und muss gerundet werden. Beim kaufmännischen Runden würde sich die Figur je nachdem, wie lange die hohen FPS anliegen, ewig nicht bewegen. Rundet man pauschal auf, bewegt sie sich zu schnell.
Da gibt es jetzt 3 Varianten. Variante 1 ist das festsetzen der FPS auf 30. Bei sinkenden Frames kann der Faktor die Differenzen problemlos ausgleichen. Aber bei 2D Spielen muss der Rechner schon arg schlecht sein, um keine 30 FPS zu schaffen. Man könnte also theoretisch auf den Faktor verzichten. Variante 2 wäre, die Framesteps nach der Wahrscheinlichkeit ausreichend hoch zu wählen. Deutet man 30 FPS als 100% und liegt die Wahrscheinlichkeit hoch, das so gut wie nie mehr wie 300 FPS erreicht werden können, sollte kein Step kleiner als 10 sein. Beim kaufmännischen Runden würde sich das ganze dann auch einigermaßen ausgleichen. Führt aber dazu, das langsame Bewegungen unter 300 Pixel die Sekunde ausgeschlossen sind. Die komplexeste, aber dafür beste Möglichkeit wäre es, einen zusätzlichen Taktgeber zu verwenden, der die Steps auch bei langsamen Bewegungen glättet (Im Grunde eine Art Frameskipper für die Steps). Ist aber eine tierische Fummelarbeit.
Ich würde also auf 60 FPS festsetzen und die Steps an 30 FPS ausrichten. Drops kann man dann mit einem Faktor ausgleichen. Damit wären dann Steps von Minimum 2 möglich. Das sollte auch für langsamere Bewegungen ausreichen. Sollten die internen Prozeduren zum locken der Framerate nicht funktionieren, dann kann man das auch über ein Delay machen:
Code: Alles auswählen
Define.b FirstRun
Define.i FrameTime, Framestart, FrameLength
#GAME_FRAMELOCK = 60 ; Durch den Wert ersetzen, auf den festgesetzt werden soll
FirstRun = #True ; Prüft auf den ersten Durchlauf
FrameTime = Round(1000 / #GAME_FRAMELOCK, #PB_Round_Nearest) ; Errechnet die Frametime bei 60 FPS
Define.i TestRounds, Tick, Temp
TestRounds = 2000
Tick = 0
OpenConsole()
TimestampA.i = ElapsedMilliseconds()
For i = 0 To 10
Repeat
; = START = Delay für den Lock
If FirstRun = #True
FirstRun = #False
Else
If FrameLength < FrameTime
Delay(FrameTime - FrameLength)
EndIf
EndIf
; = END = Delay für den Lock
FrameStart.i = ElapsedMilliseconds() ; Wird für die Berechnung der Rechenzeit benötigt
Delay(34) ; Simuliert Rechenzeit des Schleifendurchlaufs (bis 17 ms hat man 59/60 FPS)
FrameLength.i = ElapsedMilliseconds() - FrameStart ; Hier wird die Rechenzeit berechnet
Tick + 1
Temp = ElapsedMilliseconds() - TimestampA
Until Temp >= 1000
TimeStampA = ElapsedMilliseconds()
PrintN(Str(Tick) + " FPS und einer Worktime von " + Str(FrameLength) + " ms")
Tick = 0
Next
Input()
Den Code kann man so testen. Ist zwar jetzt über die Konsole simulierte FPS. Aber Man erkennt das Prinzip.