[gelöst] [Newbiefrage] Wie Framerate einstellen und halten?
-
In der Regel hat man eine Update Funktion. In dieser Update Funktion, vergleicht man den aktuellen Zeitpunkt mit einem Vergangenen Zeitpunkt, so kann man schonmal erfahren wie viel Zeit seit dem letzten Update vergangen ist. Diese Zeitspanne nutzt man für seine Berechnungen. Dann speichert man die aktuelle Zeit für den nächsten Aufruf von Update.
-
Ok, mit der Differenz der Zeit kann ich dann meine Animation berechnen lassen um wie viel sich z.Bsp. ein Objekt weiter bewegen muss etc., oder? Aber dann hab ich ja weiterhin x000 Frames nur das es halt immer gleich schnell läuft. Da ich ja mit SDL arbeite könnte ich SDL_GetTicks verwenden? Oder ist das 'ne blöde Idee? Wenn ich dann die Frames beschränken will könnte ich ja ausrechnen wie viele Millisekunden ein Frame brauchen muss wenn z.Bsp. 60 Frames von mir vorgegeben werden, die Zeit die ein durchlauf gebraucht hat abziehen und dann das Programm für die Restliche Zeit pausieren lassen? Aber ist das "der richtig Weg"? Macht es überhaupt Sinn die Frames konstant zu halten und nicht immer das Maximum rauszuholen?
-
SDL_GetTicks sollte geeigent sein für die Zeitmessung. Es macht in der Regel keinen Sinn die Framerate zu begrenzen. Warum auch? Lass es doch mit 3000 Frames per Second laufen, wenn es kann. Wenn du die verstrichene Zeit als Faktor benutzt, so wirst du keinen Unterschied zwischen 3000 fps und 100 fps feststellen können. Denn wenn du 3000 fps hast, so bewegen sich die Animationen nur immer ein ganz kleines Stück weiter, bei 100 fps bewegen sie sich proportional ein größeres Stück weiter. Da dein Auge konkret maximal ~32 fps registrieren kann, wird dein Auge optisch keinen Unterschied feststellen können.
Konkreter:
10 fps, die Figur bewegt sich pro Frame 1 Schritt weiter.
5 fps, die Figur bewegt sich pro Frame 2 Schritte weiter.Bei beiden Framerates hat sich nach 1 Sekunde die Figur genau 10 Schritte weiter bewegt.
-
Hmm, ich frag deshalb weil ich ein paar Spiele kenne bei denen man die Frames begrenzen lassen kann und mir subjektiv vorkommt das sie flüssiger laufen.
Danke auf jedenfall für deine Hilfe.
Gruß Daniel
-
Janjan schrieb:
Es macht in der Regel keinen Sinn die Framerate zu begrenzen. Warum auch?
Weil sonst ein Spiel, das vielleicht nur 5-10% CPU-Auslastung erzeugen würde, einen ganzen Kern auslastet. Auf einem Einkernsystem ist das fatal, auf einem Mehrkernsystem nimmt es trotzdem anderen rechenintensiven Programmen Rechenzeit weg bzw. die CPU frisst mehr Strom, wird heißer und die Lüfter drehen schneller bzw. schalten sich erst ein (bei manchen Laptops).
Wenn du von den Benutzern deines Spiels nicht zum ****** gewünscht werden willst, solltest du die Framerate besser limitieren.
-
Dafür gibt es doch VSync. Oder knall ein Sleep rein.
Ich würde dich "nicht zum ******" wünschen, hab ne Wakü.
-
Problematisch wirds, wenn das ganze auf nem Rechner läuft der dermaßen schnell ist dass die Zeitdifferenz zwischen zwei Render-Aufrufen null ergibt.
Ich würde da schon ne Bremse reinbauen (vllt. nicht grad bei "nur" 60fps)
-
Blaze schrieb:
Ich würde dich "nicht zum ******" wünschen, hab ne Wakü.
Was hat ne Wasserkühlung damit tun tun?
-
Ich limitiere meine Frames auf 100 FPS. Einfach aus dem genannten Grund, dass man nicht die komplette Leistung eines Systems verschwenden sollte, nur weil man kann. Das ist für mich schlechter Stil.
Ich berechne die vergangene Zeit zwischen 2 Frames und verwende das delta als Berechnung für die neue Position.
Als kleines Beispiel ein Schnipsel aus einem aktuellen Projekt:// Calculate new position NLVector2f vec = this->getPosition(); vec.set(vec.x() + (m_velocity_x * (delta * 0.1f)), vec.y() + (m_velocity_y * (delta * 0.1f)) );
So ist das Objekt immer gleichschnell, egal ob bei 100 FPS oder 2000 FPS.
Und das ist meine Render-Loop, mit FrameLimiter:
void NLWindowGL::enterLoop() { u32 lastTick = 0; u32 ticks = 0; u32 delta = 0; bool mouseBState = false; // Infinite Loop! while (true) { /************************************************************************/ /* Timing */ /************************************************************************/ ticks = SDL_GetTicks(); delta = ticks-lastTick; lastTick = ticks; /************************************************************************/ /* Input */ /************************************************************************/ SDL_Event evt; while (SDL_PollEvent(&evt)) { switch (evt.type) { case SDL_KEYDOWN: { m_keyEvent(evt.key.keysym.sym, true); break; } case SDL_KEYUP: { m_keyEvent(evt.key.keysym.sym, false); break; } case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: { mouseBState = (evt.button.state == SDL_PRESSED); u8 key = evt.button.button; m_keyEvent(key, mouseBState); break; } case SDL_MOUSEMOTION: { u8 key = evt.button.button; m_mousepos = NLVector2f(evt.motion.x, evt.motion.y); m_mouseEvent(evt.motion.x, evt.motion.y, key, mouseBState); break; } case SDL_QUIT: { bool quit = false; if (m_quitEvent.size() == 0) { quit = true; } if (m_quitEvent.emit() == true) { quit = true; } if ( quit ) { SDL_Quit(); return; } break; } } } /************************************************************************/ /* Clear Buffers */ /************************************************************************/ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); /************************************************************************/ /* Frame-limiter */ /************************************************************************/ if ( m_hasFrameLimit) { while ( m_tmNext > SDL_GetTicks() ) { SDL_Delay(1); } m_tmNext += (1000 / m_fps_limit); } /************************************************************************/ /* Render-Event */ /************************************************************************/ SystemController().getRenderManager().renderObject(); m_renderEvent.emit(delta); /************************************************************************/ /* OpenGL-Debugging */ /************************************************************************/ # if defined(_DEBUG) && NL_PERFORM_OPENGLCHECK == 1 u32 err = glGetError(); while (err != GL_NO_ERROR && err != m_prevGLError) { m_prevGLError = err; std::stringstream ss; ss << "[OpenGL] " << gluErrorString(err); NLError(ss.str().c_str()); err = glGetError(); } # endif /************************************************************************/ /* Framecounter */ /************************************************************************/ m_frameCounter.update(delta); /************************************************************************/ /* Swapping the buffers */ /************************************************************************/ SDL_GL_SwapBuffers(); } }
HTH
rya.
-
So, nachdem ich mir die ganzen Anmerkungen mal ne Nacht durch'n Kopf gehen lassen habe, nun mein eigener kleiner Versuch, scheint sogar zu funktionieren.
/* code */ #define cScreenFrames 80 // Gewünschte Framerate, "0" = Maximale Framerate /* code */ Uint32 Runtime = 0; // Aktuelle Laufzeit in Millisekunden Uint32 Lasttime = 0; // Letzte Laufzeit in Millisekunden Uint32 Frametime = 0; // ca. Laufzeit für 1. Frame GLfloat Factor = 0.0f; // Faktor zur Berechnung der GLScene /* code */ GLvoid FactorManager() { Lasttime = Runtime; // Übergibt die aktuelle Zeit des vorherigen Durchlauf an Lasttime Runtime = SDL_GetTicks(); // Holt die neue aktuelle Laufzeit while( Runtime - Lasttime < 1) // Soll verhindern das bei extrem schnellen Rechnern die weniger als 1 Millisekunde für einen Frame brauchen Runtime und Lasttime gleich sind und so bei der Faktor ein Fehler auftritt ( 0 / 1000 ) { SDL_Delay( 1); // Ok laut SDL Referenz warten einige Systeme mindestens 10ms, was aber nicht weiter von belang ist da auch ein Sleep von 10ms nicht schlimm wäre weil dann trotzdem noch mit 100 Frames gerendert werden würde. Runtime = SDL_GetTicks(); } if( Frametime != 0) // 0 wenn keine Framelimitierung aktiviert, ansonsten ca. (da nur Uint32) Zeit in der 1 Frame gerendert werden muss. { while( (int)Frametime - ((int)Runtime - (int)Lasttime) >= 15) // Wenn der Rechner noch mehr als 15ms zeit hätte den Frame zu rendern. Habe hier 15 statt 10 gewählt um durch das SDL_Delay auf der sicheren Seite zu sein. Lieber nen halben Frame zu viel als zu wenig... ;) { SDL_Delay( 10); Runtime = SDL_GetTicks(); } } Factor = ((GLfloat)Runtime - (GLfloat)Lasttime) / 1000.0f; } /* code */ int main( int argc, char** argv) { /* code */ if( cScreenFrames != 0) Frametime = 1000 / cScreenFrames; // Berechnet leicht ungenau (Uint32) die Zeit die für einen Frame zur verfügung steht bei der gewünschten Framerate // MainLoop: while( Quit == false) { EventHandler(); xrot += 72.0f * Factor; yrot += 36.0f * Factor; zrot += 6.0f * Factor; DrawGLScene(); FactorManager(); } /* code */ }
So, hoffe man blickt da durch?
Erledigt zumindest seinen Job...
Hab mich für die leicht ungenaue Frameberechnung entschieden da der Fehler eh so gering ist das es egal ist. Natürlich kann man sagen, wenn ich 80 Fps eingebe, will ich auch 80 haben, aber ich hab mir gedacht das 80.2 oder so auch ok sind...