28

SEP09

Mainloop, FPS, Framebremse und VSync …

Trackback URIVon Peppie in C++, Programmierung, Spiele-Entwicklung

Sehr interessante Themen in der Spieleentwicklung, vorallem bei Anfängern. Was ist eine Mainloop? Wie zeige ich die FPS von meinem Spiel an? Meine Spielfiguren haben die Lichtgeschwindigkeit entdeckt!?! und was zum Teufel ist VSync oO Dies und noch mehr sind sehr beliebte Anfängerfragen auf diesem Gebiet, ich selbst stand damals genauso da :) aber genau um diese Themen geht es in meinem heutigen Blogbeitrag.

Lasst uns zunächst mit der Mainloop beginnen, jeder von euch wird dieses “Kernelement” jeder Win32 Anwendung bestimmt kennen, meist sieht sie wie fogt aus

1
2
3
4
5
6

while( GetMessage( &WindowMessages, NULL, 0, 0 ) )
{
    TranslateMessage( &WindowMessages );
    DispatchMessage( &WindowMessages );
}

Auch wenn sie den anschein einer Endlosschleife hat, ist sie etwas besonderes, denn sie lastet den Rechner einen Kern nicht mit 100% aus. Wie kommt das? Das liegt daran das die Funktion getMessage in einem Schlafmodus ist solange sie vom Betriebssystem keine Nachrichten bekommt. Sobald eine eintrifft wacht diese Funktion auf und es wird die Nachricht abgearbeitet. hmm schlafen … ein schlechtes Keyword in der Spieleprogrammierung, denn das Spiel würde erst dann weiterlaufen wenn von Windows eine Message bearbeitet wird. Das würde ein wahres Ruckelchaos werden :)

Wie müsste also eine solche Mainloop für Spiele aussehen?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Steuerung der Endlosschleife
bool bQuit = false;

while( !bQuit )
{
    if( PeekMessage( &WindowMessages, NULL, 0, 0, PM_REMOVE ) )
    {
        if( WindowMessages.message == WM_QUIT )
        {
            bQuit = true;
        }
        else
        {
            TranslateMessage( &WindowMessages );
            DispatchMessage( &WindowMessages );
        }
    }
    else
    {
        // Frame neu malen
    }
}

Nun haben wir hier aber wieder das klassische Beispiel einer “Endlosschleife”. Statt zu warten bis wir eine Nachricht bekommen, schauen wir bei jedem Durchgang mit PeekMessage ob es eine Nachricht für uns gibt, wenn ja, dann soll sie rausgeholt werden und mit PM_REMOVE aus der Message-Queue entfernt werden. Sobald wir den Befehl WM_QUIT bekommen, wird die Endlosschleife beendet und das Programm läuft weiter in der Main-Funktion. Wenn wir keine Nachricht bearbeiten dann soll unser Programm immer und immer wieder ein Bild malen, dies sind unsere sogenannten Frames.

FramesPerSeconds (FPS)

Man kann sich ja schon denken das unsere eben gelernte Mainloop relativ schnell arbeiten wird, lassen wir uns doch mal berechnen / anzeigen wie schnell sie ist.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
TCHAR* calcFPS()
{
    static UINT uiFramerate    = 0;
    static UINT uiLastTime    = GetTickCount();
    static TCHAR szFPS[10]    = _T("0/s");

    UINT uiCurrentTime = GetTickCount();
    UINT uiElapsedTime = uiCurrentTime - uiLastTime;

    // Framecounter jedesmal um eins erhöhen
    uiFramerate++;

    // prüfen ob wir schon über einer Sekunde sind
    if( uiElapsedTime >= 1000 )
    {
        // framerate pro sekunde in szFPS ablegen
        swprintf_s( szFPS, 7, _T("%u/s"), static_cast<UINT>( uiFramerate * 1000.0 / uiElapsedTime ) );

        // Zeit der letzten Berechnung speichern
        uiLastTime = uiCurrentTime;

        // Framerate wieder zurücksetzen für neue Berechnung
        uiFramerate = 0;
    }

    return szFPS;
}

Baut diesen Funktionsaufruf in unsere Mainloop an die Stelle wo er ein Frame malen soll. In jedem durchgang wird die Variable “uiFramerate” um eins erhöht bis wir 1000 Millisekunden bzw. 1 Sekunde nach dem ersten Aufruf erreicht haben. Dann berechnen wir die FramesPerSeconds und speichern diese in die Variable “szFPS”, die wir auch bei jedem Aufruf zurückgeben lassen.

Hinweis: Lasst nicht die Mainloop mit nur diesem Aufruf laufen, es könnte das System auslasten

Hier ein kleines Beispiel wir ihr die FPS anzeigen lassen könnt, geht dazu über die Mainloop und erstellt ein Device Context. Auf das erstellte Device Context malen wir dann die FPS als Text drauf. Das zusätzlich definierte RECT Objekt gibt die Position unseres Textes an.

1
2
3
4
5
6
7
8
9
...
UpdateWindow( WindowHandle );

HDC hdc = GetDC( WindowHandle );
RECT Rectangle = { 10, 100, 100, 120 };

while( !bQuit )
{
...

Geht danach in unsere Mainloop an die Stelle wo wir das Frame zeichnen und benutzt die Funktion “DrawText” um einen Text ausgeben zu lassen.

1
2
3
4
5
6
7
8
...
}
else
{
    // Frame neu malen
    DrawText( hdc, calcFPS(), 32, &Rectangle, DT_LEFT );
}
...

Als Zeichenkette geben wir direkt unsere Funktion calcFPS() Funktion an, ihr könnt natürlich auch die calcFPS() vorher aufrufen und die FPS-Zeichenkette in einen TCHAR Array speichern. Wenn ihr die Anwendung jetzt kompiliert und ausführt, sehr ihr eure FramesPerSeconds. Ich komm auf gut 4.600 FPS joa da wird jeder Counterstrike-Spieler neidisch werden ABER wir machen ja noch nichts großes in unserer Mainloop, bildet euch also nicht ein das dies die ganze Zeit so bleiben wird ^^

Wenn ihr jetzt euer Spiel teilweise programmiert habt, werdet ihr eventuell feststellen das die Objekte mit einem mega Speed über den Bildschirm fliegen. Auf der nächsten Seite zeige ich euch mehrere Varianten einer Framebremse und wie man es am besten machen sollte.

Seite:
1 2

Peppie
Über den Autor:
Vor mehr als 7 Jahren habe ich mein Hobby zum Beruf gemacht. Seit her bekommt mich kein Problem so schnell in die Knie, ich bin sehr verbissen und arbeite solange an einem Problem bis es gelöst ist.

Ähnliche Artikel:

1 Kommentare

Maurice

Diese Funktion:
swprintf_s( szFPS, 7, _T(“%u/s”), static_cast( uiFramerate * 1000.0 / uiElapsedTime ) );

ist leider nicht verwendbar, da diese als ersten parameter wchar_t* erfordert.

verwende bitte die funktion sprintf_s


Kommentar schreiben

;) :( :) :D :P :o :| ^^ :> :< :cry: :S xD


Blogverzeichnis - Blog Verzeichnis bloggerei.de frisch gebloggt Blog Top Liste - by TopBlogs.de Bloggeramt.de Add to Technorati Favorites Dennis bei Xing Wikio - Top Blog UrlFan.com