Seite 1 von 2
Frameunabhängig programmieren
Verfasst: 27.03.2007 07:04
von X0r
Hallo!
Ich will euch in diesem kurzen "Tutorial" zeigen, wie man in frameunabhängig programmiert.
Wofür ist das gut?
Frameunabhängiges Programmieren sorgt dafür, dass eure Programm auf allen Rechner gleich schnell laufen. Natürlich läuft es nicht wirklich gleichschnell.
Der Trick ist ganz einfach. Man ermittelt die Zeit nach dem windowsstart am Anfang der Schleife. Am Ende ermittelt man ihn nochmal und speichert die Differenz vom neuen Wert und vom alten Wert in eine Variable. Wenn man nun zum Beispiel in einem Spiel sein Auto um 1 LE auf der x-Achse bewegen will, schreibt man:
MoveMesh(car,1*differenz,y,z).
Hier ein Beispiel:
Code: Alles auswählen
OpenConsole()
Repeat
times=gettickcount_()
;Delay(2000)
a=a+1*time
PrintN(Str(a))
time=gettickcount_()-times
ForEver
CloseConsole()
Falls ihr es wirklich testen wollt, dann kompiliert den code ohne "Delay(2000)" und einmal mit "Delay(2000)" und ihr werdet sehen..
In Spielen, vor allem in Online-Spielen, sollte man IMMER frameunabhängig programmieren!
Viele denken:"Ach was, muss nicht sein". Muss wohl sein. Wenn man in seinem Spiel eine Zeit programmiert, mit der bestimmte Sachen im Spiel passieren sollen, kann es dann zu ernsthaften Problemen führen.
Edit:
Hier noch ein "Zeit"-Beispiel:
Code: Alles auswählen
OpenConsole()
Repeat
times=gettickcount_()
;delay(2000)
a=a+1*time
If(Str(a/1000)<>la.s)
PrintN(Str(a/1000))
la.s=Str(a/1000)
EndIf
time=gettickcount_()-times
ForEver
CloseConsole()
Wieder könnt ihr delay nehmen und werdet sehen, dass es "genauso schnell wie ohne delay läuft".
Verfasst: 27.03.2007 13:42
von AND51
Kann man dafür nicht auch SetFrameRate() nehmen?
Verfasst: 27.03.2007 13:55
von Thalius
AND51 hat geschrieben:Kann man dafür nicht auch SetFrameRate() nehmen?
Nur wenn VSync aktiviert ist und du auch was auf dem Screen zeichnest. Zb. die meisten Windowed apps arbeiten ohne Vsync - sprich 100% CPU Last.
gettickcount_() iss ausserdem genauer - Die Genauigkeit kommt zb. bei movements im 3D Space zum tragen, da du dort 32 bit floats verwendest ( noch ...

.
Thalius
Verfasst: 27.03.2007 15:43
von THEEX
Allerdings ist gettickcount_() nicht gerade sonderlich genau, für genauere Messungen ist davon abzuraten! Die Unterschiede könne durchaus 10 - 20 ms betragen.
Verfasst: 27.03.2007 15:56
von AND51
Richtig, CSprengel. GetTickcount_() ist ziemlich dasselbe wir ElapsedMilliseconds(), und beide haben ihre Ungenauigkeit. Da muss man schon hochauflösende Timer wie QueryPerformanceCounter_() benutzen.
Thalius, VSync kannst du in PB ganz einfach mit FlipBuffers() ein- und ausscahlten; außerdem kannst du sogar seit PB 4 damit einen VSync-Modus einschalten, der eine CPU-schonendere Variante enthält.
Forge, deine Gedanken sind schon ok, aber an deiner Stelle würde ich dann doch eher mit den hochauflösenden Timer arbeiten oder einfach ganz auf FlipBuffers() und SetFrameRate() umsteigen.
Verfasst: 27.03.2007 15:59
von Kaeru Gaman
völlig richtig, CSprengel
die bessere methode ist meiner meinung nach, für die abfolge im spiel einen zeittakt anzulegen.
sagen wir mal, alle 20ms soll das game einen "schritt" machen.
dann sieht das gerüst so aus:
Code: Alles auswählen
#timestep = 20
timer = GetTickCount_()
Repeat
If GetTickCount_() > timer
GameStep()
timer + #timestep
EndIf
Until EXIT
dadurch, dass der timer nirgendwo in der schleife auf einen aktuellen GetTickCount_() gesetzt wird,
sondern dass er jeweils um einen festen wert erhöht wird,
ist eine ungenauigkeit von GetTickCount_() vernachlässigbar.
GameStep() wird vielleicht mal 10ms früher oder später aufgerufen,
aber innerhalb einer stunde wird er ziemlich genau so oft aufgerufen werden wie er soll.
(in diesem fall 180.000 mal +/- 3mal)
PS:
war ElapsedMilliseconds() jetzt der wrapper von GetTickCount_(), oder von TimeGetTime_()?
Verfasst: 27.03.2007 16:14
von Thalius
Thalius, VSync kannst du in PB ganz einfach mit FlipBuffers() ein- und ausscahlten; außerdem kannst du sogar seit PB 4 damit einen VSync-Modus einschalten, der eine CPU-schonendere Variante enthält.
Da sieht man wie lange ich nix mehr mit PB's Grafikfunktionen gemacht habe ...
Muss ich mal testen... und das gettickcount_() nicht das genauste ist - iss klar

aber für sowas wie nen "Gametic" isses ideal.
elapsedmilliseconds() == gettickcount_()
Thalius
Verfasst: 27.03.2007 17:44
von a14xerus
setframerate beschränkt aber nur die geschwindigkeit bei schnellen pc's , macht das spiel auf alten pc's aber nicht schneller

Verfasst: 27.03.2007 17:47
von Kaeru Gaman
stimmt genau...
die beste lösung ist immernoch, unterschiedliche threads für darstellung und berechnung zu verwenden...
die berechnung wird getimed, so wie von mir vorgeschlagen oder mit nem timer-callback,
die darstellung läuft durch ohne SetFrameRate. notfalls kann man nen "verändert"-flag von der game routine setzen lassen, um ein neuzeichnen einer unveränderten darstellung zu umgehen.
note: wenn irgendwo ein thread auf nen flag warten soll, die Delays nicht vergessen, um die CPU nicht zu braten....
Verfasst: 30.03.2007 20:49
von X0r
Irgendjemand sagte:
>die beste lösung ist immernoch, unterschiedliche threads für darstellung und berechnung zu verwenden...
Wobei man bei threads und DirectX aufpassen muss(Außer man will nicht DirectX rendern lassen).
Noch eine Denkweise:
Code: Alles auswählen
Global FPS_MAX = 60
FramePeriod = 1000/FPS_MAX
FrameTime = GetTickCount_() - FramePeriod
repeat
repeat
FrameEllapsed = GetTickCount_() - FrameTime
Until FrameEllapsed
FrameTicks = FrameEllapsed/FramePeriod
FrameTween# = Float(FrameEllapsed Mod FramePeriod) / Float(FramePeriod)
For FrameLimit = 1 To FrameTicks
FrameTime = FrameTime + FramePeriod
UpdateWorld()
Next
forever
Nun, viele Spieleentwickler, die ich kenne, machen es mit Gettickcount_(). Ob es auf die paar Millisekunden wirklich ankommt.....darüber lässt sich streiten.
Nun poste ich noch ein Beispiel zum ermitteln der FPS:
Code: Alles auswählen
repeat
frames=frames+1
if gettickcount_()-ltime>1000
fps=frames
frames=0
ltime=gettickcount_()
endif
forever
AND51 sagte:
>FlipBuffers() und SetFrameRate()
FlipBuffers(), nun ja, tauscht 2 Buffers aus(Frontbuffer mit aktuellem Buffer), und?
SetFrameRate? Kann sein, solch eine Funktion kannte ich bisher noch nicht.
Diese OGRE Engine scheint es einem wirklich leicht zu machen.
Werde mal einen Blick drauf werfen.
>setframerate beschränkt aber nur die geschwindigkeit bei schnellen pc's , macht das spiel auf alten pc's aber nicht schneller
Nein? Wirklich schade. Dachte, dass das Programm dann konsequent mit X fps läuft.
P.S: Schneller wird dadurch nix.

Wär schön. Du meinst das bestimmt anders.