Screen aktualisiert viel zu schnell trotz SetFrameRate()

Fragen zu Grafik- & Soundproblemen und zur Spieleprogrammierung haben hier ihren Platz.
Benutzeravatar
Fluid Byte
Beiträge: 3110
Registriert: 27.09.2006 22:06
Wohnort: Berlin, Mitte

Screen aktualisiert viel zu schnell trotz SetFrameRate()

Beitrag von Fluid Byte »

Wenn man mit SetFrameRate() die FPS auf 59 oder 61 setzt passt die Geschwindigkeit. Ist sie genau auf 60 erfolgt gar keine Drosselung und es wird so schnell aktualisiert wie die Hardware es hergibt. Aber genau das möchte ich verhindern. Ich will mein Timing nicht an die Displayrate koppeln (60Hz / 120hz Monitor) sondern selbst regeln.

Code: Alles auswählen

#WIDTH = 800
#HEIGHT = 600
#VSYNC = 0

InitSprite() : InitKeyboard()

OpenWindow(0,0,0,#WIDTH,#HEIGHT,"",#PB_Window_SystemMenu | #PB_Window_ScreenCentered)

If #VSYNC
   OpenWindowedScreen(WindowID(0),0,0,#WIDTH,#HEIGHT,0,0,0)
Else
   OpenWindowedScreen(WindowID(0),0,0,#WIDTH,#HEIGHT,0,0,0,#PB_Screen_NoSynchronization)   
   SetFrameRate(60) ; 59 geht, 61 geht, 60 geht nicht
EndIf

LoadSprite(0,#PB_Compiler_Home + "\Examples\Sources\Data\Geebee2.bmp")
TransparentSpriteColor(0,RGB(255,0,255))
CreateSprite(1,#WIDTH,#HEIGHT)
StartDrawing(SpriteOutput(1))
For i=0 To #HEIGHT
   c = i * 100 / #HEIGHT
   Box(0,i,#WIDTH,1,RGB(0+c,20+c,50+c))
Next
StopDrawing()

Repeat
   EventID = WindowEvent()
   
   Angle.f + 0.05   
   SX = #WIDTH / 2 - SpriteWidth(0) / 2
   SY = #HEIGHT / 2 - SpriteHeight(0) / 2
   
   ClearScreen(0)
   DisplaySprite(1,0,0)
   DisplayTransparentSprite(0,SX + Cos(Angle) * 50,SY + Sin(Angle) * 50)
   FlipBuffers()
Until EventID = #PB_Event_CloseWindow
Windows 10 Pro, 64-Bit / Outtakes | Derek
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Re: Screen aktualisiert viel zu schnell trotz SetFrameRate()

Beitrag von ccode_new »

Einen schönen Tag!

Das ist ja mal verrückt ?!

Also wenn ich:

OpenWindowedScreen(WindowID(0),0,0,#WIDTH,#HEIGHT,0,0,0,#PB_Screen_SmartSynchronization)

oder

OpenWindowedScreen(WindowID(0),0,0,#WIDTH,#HEIGHT,0,0,0,#PB_Screen_WaitSynchronization)

schreibe funktioniert es,

aber bei:
OpenWindowedScreen(WindowID(0),0,0,#WIDTH,#HEIGHT,0,0,0,#PB_Screen_NoSynchronization)

rotiert es einfach nur wie verrückt.

Das liegt wohl irgend wie an der "Synchronization".
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
Benutzeravatar
#NULL
Beiträge: 2235
Registriert: 20.04.2006 09:50

Re: Screen aktualisiert viel zu schnell trotz SetFrameRate()

Beitrag von #NULL »

Bei mir rotiert es immer gleich, auch bei NoSync. Ist halt auch abhängig von Treiber und Einstellungen. Aber wenn er sich bei genau 60 plötzlich wie NoSync verhält, dann ist ja wohl was faul.

Aber wenn deine Berechnungen von der Framerate unabhängig sein sollen, dann brauchst du dich eigentlich auch gar nicht um die Framerate kümmern:

Code: Alles auswählen

Repeat
   EventID = WindowEvent()
   
   Angle.f + Radian(360) * perSecond.f        ; one turn per second
   SX = #WIDTH / 2 - SpriteWidth(0) / 2
   SY = #HEIGHT / 2 - SpriteHeight(0) / 2
   
   ClearScreen(0)
   DisplaySprite(1,0,0)
   DisplayTransparentSprite(0,SX + Cos(Angle) * 50,SY + Sin(Angle) * 50)
   FlipBuffers()
   
   frameDuration = ElapsedMilliseconds() - frameFinished
   frameFinished = ElapsedMilliseconds()
   perSecond.f = frameDuration / 1000

   Delay(Random(100))                         ; simulate unstable framerate
  
Until EventID = #PB_Event_CloseWindow
http://www.purebasic.fr/english/viewtop ... 59#p496697
my pb stuff..
Bild..jedenfalls war das mal so.
Benutzeravatar
TroaX
Beiträge: 659
Registriert: 08.03.2013 14:27
Computerausstattung: PC: Ryzen 9 3950X, 96 GB RAM, RX6800XT, 2.5 TB SSD, 21:9 Display, Pop_OS! | Lappi: Ryzen 7 5800H, 16 GB RAM, 1 TB SSD, Pop_OS!
Wohnort: NRW
Kontaktdaten:

Re: Screen aktualisiert viel zu schnell trotz SetFrameRate()

Beitrag von TroaX »

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.
PC: Ryzen 9 3950X | 96 GB RAM | RX6800XT | 2,5 TB NVMe | Pop_OS!
Notebook: 16" 3:2 | Ryzen 7 5800H | 16 GB RAM | Radeon Vega | 1TB NVMe | Pop_OS!
NAS: Fritz.Box :lol:
Coding: Purebasic 6.04 | PHP | HTML | CSS | Javascript
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Re: Screen aktualisiert viel zu schnell trotz SetFrameRate()

Beitrag von ccode_new »

Hallo,

solche "Rechnereien" der Framerate sind meist der größte Mist den man machen kann.

Man sollte möglichst für ein Spiel, etc. die Framerate für die Step's auf einen konstanten Wert legen. (z.B. 30, 60, ...)

Das Spiel selber sollte man dann an die Framerate anpassen.

Läuft dein Spiel zu langsam liegt es dann entweder an einer falschen Anpassung oder die Mindestanforderung der Hardware wurde nicht erfüllt (schafft zu wenig Frames).
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
Benutzeravatar
TroaX
Beiträge: 659
Registriert: 08.03.2013 14:27
Computerausstattung: PC: Ryzen 9 3950X, 96 GB RAM, RX6800XT, 2.5 TB SSD, 21:9 Display, Pop_OS! | Lappi: Ryzen 7 5800H, 16 GB RAM, 1 TB SSD, Pop_OS!
Wohnort: NRW
Kontaktdaten:

Re: Screen aktualisiert viel zu schnell trotz SetFrameRate()

Beitrag von TroaX »

ccode_new hat geschrieben:solche "Rechnereien" der Framerate sind meist der größte Mist den man machen kann.
Begründung? Denn das lasse ich so nicht stehen, ohne nachgebohrt zu haben.
PC: Ryzen 9 3950X | 96 GB RAM | RX6800XT | 2,5 TB NVMe | Pop_OS!
Notebook: 16" 3:2 | Ryzen 7 5800H | 16 GB RAM | Radeon Vega | 1TB NVMe | Pop_OS!
NAS: Fritz.Box :lol:
Coding: Purebasic 6.04 | PHP | HTML | CSS | Javascript
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Re: Screen aktualisiert viel zu schnell trotz SetFrameRate()

Beitrag von ccode_new »

Hi TroaX :D

Ok!
TroaX hat geschrieben: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.
Das wäre ja ein Punkt.

Wenn man verschiedene (z.B. höhere) Bildraten zu lassen möchte muss man natürlich anpassen.

Ansonsten ist es doch flexibler mit einer konstanten Room-Speed/Framerate/Steprate/... zu arbeiten.

Natürlich beginnt damit die "Rechnerei" auf Basis deiner eingestellten Rate (100%).
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
Benutzeravatar
TroaX
Beiträge: 659
Registriert: 08.03.2013 14:27
Computerausstattung: PC: Ryzen 9 3950X, 96 GB RAM, RX6800XT, 2.5 TB SSD, 21:9 Display, Pop_OS! | Lappi: Ryzen 7 5800H, 16 GB RAM, 1 TB SSD, Pop_OS!
Wohnort: NRW
Kontaktdaten:

Re: Screen aktualisiert viel zu schnell trotz SetFrameRate()

Beitrag von TroaX »

ccode_new hat geschrieben:Wenn man verschiedene (z.B. höhere) Bildraten zu lassen möchte muss man natürlich anpassen.

Ansonsten ist es doch flexibler mit einer konstanten Room-Speed/Framerate/Steprate/... zu arbeiten.

Natürlich beginnt damit die "Rechnerei" auf Basis deiner eingestellten Rate (100%).
Genau deswegen habe ich ja geschrieben, wie ich es machen würde. Ich würde die FPS auf maximal 30 oder 60 setzen, würde mich an der Rate der umfangreichsten Animation orientieren (zum Beispiel 15 FPS - Besser wäre eher das doppelte, da man mehr Luft bekommt) und würde daraufhin meine Stepwerte auswählen (in diesem Falle wäre der Basiswert minimum 4, bei 30 wären es 2 usw.), damit die langsamste Bewegung selbst bei 60 FPS bei jedem Frame minimum 1 Pixel/Grad Bewegung hat und sich durch das Runden nicht verschluckt. Die Sprites sollen ja am Ende da landen, wo sie sein sollten ;)

Und wenn der Framelock über SetFrameRate nicht funktioniert, muss man ihn eben selbst implementieren. Und das geht, in dem man eben bei über X Bildern die Sekunde die Rechenzeit für den Frame von der Frametime, wie sie bei X Bildern die Sekunde sein sollte, subtrahiert. Das geht zwar nie immer 100% genau, weil Delay mit einer Ganzzahl arbeitet und deswegen man bei 60 FPS Lock meist 58 bis 60 FPS herauskommen. Aber das gleicht sich mit der Verlustzeit in den Routinen wieder einigermaßen aus. Auf meinem langsameren Subnotebook waren es 59/60 FPS, während es auf meinem i5 58/59 FPS waren. Der schnellere hat eben die Prozeduren zur Framerate-Erfassung schneller durchbekommen, während der langsamere eben noch ne Runde drehen musste. Mit einem zweiten Thread zur Erfassung würden wir auf beiden Systemen identische Ergebnisse bekommen :lol:

Mit konstanten Raten ist man eben nicht flexibler. Das ist es ja eben. Du legst einfach die minimale und maximale Framerate fest, errechnest daraus einen Basisfaktor und ermittelst mit der realen Framerate den Faktor für die Berechnungen. Und so lange du bei Positionsänderungen oder Winkeln etc. immer daran denkst, diesen mit dem Faktor zu verrechnen, brauchst du dich bei variablen Bildraten um nichts mehr kümmern und das Gameplay wird fast durchweg mit dem selben Tempo ermöglicht, obwohl das System in den Frames einbricht.
Zuletzt geändert von TroaX am 02.07.2017 19:01, insgesamt 1-mal geändert.
PC: Ryzen 9 3950X | 96 GB RAM | RX6800XT | 2,5 TB NVMe | Pop_OS!
Notebook: 16" 3:2 | Ryzen 7 5800H | 16 GB RAM | Radeon Vega | 1TB NVMe | Pop_OS!
NAS: Fritz.Box :lol:
Coding: Purebasic 6.04 | PHP | HTML | CSS | Javascript
ccode_new
Beiträge: 1214
Registriert: 27.11.2016 18:13
Wohnort: Erzgebirge

Re: Screen aktualisiert viel zu schnell trotz SetFrameRate()

Beitrag von ccode_new »

8) TroaX

Passt!
Ich stimme dir vollkommen zu.
Betriebssysteme: div. Windows, Linux, Unix - Systeme

no Keyboard, press any key
no mouse, you need a cat
Benutzeravatar
Fluid Byte
Beiträge: 3110
Registriert: 27.09.2006 22:06
Wohnort: Berlin, Mitte

Re: Screen aktualisiert viel zu schnell trotz SetFrameRate()

Beitrag von Fluid Byte »

Interessant zu lesen und nützliche Code-Beispiele nur bleibt die Frage warum SetFrameRate() nicht funktioniert wie es soll. Das sieht für mich klar nach einem Bug aus es sei denn ich bin einem ernsthaften Logikfehler unterlegen.

Es geht letztendlich um einen vertikalen 2D-Shooter im Retro-Still der mit 60fps laufen soll. Normalerweise kann man davon ausgehen das 90% der Menschen mit einem 60Hz Monitor unterwegs sind. Nur gibt es eben auch welche mit 120hz-Monitor oder mit uralten CRT's mit seltenen Raten von 50, 59 oder 75 Hz.
Zuletzt geändert von Fluid Byte am 05.07.2017 21:51, insgesamt 4-mal geändert.
Windows 10 Pro, 64-Bit / Outtakes | Derek
Antworten