Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Snorky

Frischling

  • »Snorky« ist der Autor dieses Themas

Beiträge: 34

Wohnort: Berlin

Beruf: Student

  • Private Nachricht senden

1

15.08.2003, 16:19

tbDoMessageLoop und eine Frameratenbremse

Und wieder bin ich dabei in der Engine rumzupfuschen. Man sollte mich verbieten :P

Diesmal würde ich gerne eine Frameratenbremse einbauen, denn für 2D-Spiele ist es unter Umständen besser, wenn man sich auf eine konstante Maximalframerate verläßt und Sprite-Animationen fest vorgeben kann, als jedes Mal beim Rendern die Zwischenschritte zu berechnen.
Diese Bremse soll dann immer leer laufen (ja ich weiß, vergeudete Rechenpower, aber was solls), wenn der Rechner zu schnell arbeitet, also muss sie in die tbDoMessageLoop rein. Soweit so gut, alles schnell getan, bevor ich aber die umgebaute Funktion hier poste, bewegen mich noch einige Fragen, die ich vorher klären möchte, bevor ich wieder versuche im Nachhinein alles rückgängig zu machen. Also David, kannst Du mich bitte wieder aufklären:

Warum sind die Variablen dTime, tb_g_dFrequency und tb_g_dNumSecsPassed vom Typ double und keine float-Werte, bzw. warum sind die anderen float-Werte (z.B. pMoveProc)(float) etc) keine double-Werte? Ist es nicht irgendwie blöd immer hin und her zu casten (ein messbarer Geschwindigkeitsunterschied dürfte das ja nicht sein)? Wenn ich die alle auf float setze gibts (zumindest bei mir) keine Probleme. Kann es bei tbDirectSound.cpp Probleme geben, denn da wird auf die Werte auch zugegriffen oder kann ich beruhigt alles auf float setzen? ???

Zu guterletzt möchte ich hier noch auf ein kleines vergessenes else-Wörtchen hinweisen, das mir aufgefallen ist:
In der tbDoMessageLoop() im Bewegungszweig macht die eine if-Zeile keinen Sinn, wenn kein else folgt:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
            // Bewegungen durchführen, falls gewünscht
            if(pMoveProc != NULL)
            {
                if(dTime == 0.0)
                {
                    QueryPerformanceCounter((LARGE_INTEGER*)(&llEndTime));
                    if(llEndTime == llStartTime) dTime = 0.0001;
                    dTime = (float)(llEndTime - llStartTime) / tb_g_dFrequency;
                }

                if(pMoveProc((float)(dTime)) == TB_STOP) break;
            }

sollte heißen

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
            // Bewegungen durchführen, falls gewünscht
            if(pMoveProc != NULL)
            {
                if(dTime == 0.0)
                {
                    QueryPerformanceCounter((LARGE_INTEGER*)(&llEndTime));
                    if(llEndTime == llStartTime) dTime = 0.0001;
                    else dTime = (float)(llEndTime - llStartTime) / tb_g_dFrequency;
                }

                if(pMoveProc((float)(dTime)) == TB_STOP) break;
            }


So, dann warte ich mal auf Antwort :sleep:

2

15.08.2003, 17:01

Ich probiers mal aus.
Ich konnte aber deine erste Engine-Erweiterung nicht ausprobieren, weil ich hunderte Fehler erhalten habe. :crying:
ebah rutangiS reniem ni relheF 01 rebü hci ssad, etniem latkraF!

Snorky

Frischling

  • »Snorky« ist der Autor dieses Themas

Beiträge: 34

Wohnort: Berlin

Beruf: Student

  • Private Nachricht senden

3

15.08.2003, 17:21

Fehler? Welche Fehler? Bitte ein bischen genauer beschreiben, vielleicht hab ich irgendwas vergessen zu erwähnen, oder so :(

4

15.08.2003, 19:31

Häää?
Wenn ich die Tribase-Engine kompiliere gibt der mir bei JEDER cpp Datei folgende Fehlermeldung:

Quellcode

1
f:\Projekte\Programmier Projekte\Scherfgens Buch\Tribase\Include\tb2DVolumeSprite.h(5) : error C2017: Ungültige ESCAPE-Sequenz


Es ist halt bei JEDER Datei nicht nur von Smoky!
ebah rutangiS reniem ni relheF 01 rebü hci ssad, etniem latkraF!

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

5

15.08.2003, 19:55

Re: tbDoMessageLoop und eine Frameratenbremse

Zitat von »"Snorky"«

Warum sind die Variablen dTime, tb_g_dFrequency und tb_g_dNumSecsPassed vom Typ double und keine float-Werte, bzw. warum sind die anderen float-Werte (z.B. pMoveProc)(float) etc) keine double-Werte? Ist es nicht irgendwie blöd immer hin und her zu casten (ein messbarer Geschwindigkeitsunterschied dürfte das ja nicht sein)?

Das kann ich Dir erklären. Als ich den 3D-Sound einbaute, merkte ich, dass ich mit timeGetTime nicht mehr auskomme, um die Geschwindigkeit einer 3D-Soundquelle zu berechnen - es war zu ungenau. Da bin ich auf den High-Performance-Timer umgestiegen. Dessen Frequenz ist viel höher als die von timeGetTime (timeGetTime "tickt" 1000x pro Sekunde).
Bei den vielen Rechnungen, die da anfallen, dachte ich mir, dass float zu ungenau wäre, bei solch winzigen Zahlen, die da auftreten.
Für's Bewegen und Rendern ist es wurscht, da sind die Zahlen in einem normalen Bereich.

@lukuku:
Poste mal die Zeile aus der Datei, die Probleme macht (auf die Idee hättest Du übrigens auch selbst schomal kommen können). Ach ja, und bitte nicht sagen "Reg Dich nicht auf!" ;)

Snorky

Frischling

  • »Snorky« ist der Autor dieses Themas

Beiträge: 34

Wohnort: Berlin

Beruf: Student

  • Private Nachricht senden

6

16.08.2003, 15:48

Die Frameratenbremse

Jo, danke David,
es scheint also seine Berechtigung zu besitzen, also habe ich meine Finger davon gelassen ;)

Nun aber zur Frameratenbremse. Dazu habe ich die tbDoMessageLoop() umgeschrieben. Jetzt verhält sie sich wie vorher auch, nur hat noch besagte Erweiterung bekommen. Man kann die alte also durch diese ersetzen und weiterhin wie gehabt verwenden, ohne Einschränkungen zu erfahren (sollte zumindest so sein). Wem das aber trotzdem nicht ganz geheuer ist, kann sie ja nur einfügen und in z.B. tbDoMessageLoopBrake() umtaufen, so dass man zwischen beide im Hauptprogramm wählen kann.
Zur umgeschriebenen tbDoMessageLoop() habe ich noch eine kleine Funktion namens tbSetFramebrake() eingeführt, mit der man auch während des Spiels noch die Bremse umstellen kann.
Kommen wir zu den Änderungen.

in der TriBase.h:
unter den globalen externen Variablen folgende einfügen:

Quellcode

1
extern double   tb_g_dFramebrake;       // Framebremse, die maximale Framezahl umgerechnet mit Kehrwert


die alte tbDoMessageLoop() durch folgendes ersetzen (bzw. diehier umbenennen):

Quellcode

1
2
3
4
TRIBASE_API tbResult tbDoMessageLoop(tbResult (* pMoveProc)(float), 
                                     tbResult (* pRenderProc)(float),
                                     double dMaxFPS = 1000);                                            // Nachrichtenschleife
TRIBASE_API tbResult tbSetFramebrake(double dFramebrake);                                               // Setzen der neuen Frameratenbegrenzung


in der TriBase.cpp:
folgende globale Variable einfügen

Quellcode

1
double      tb_g_dFramebrake = 1000.0;  // Framebremse, die maximale Framezahl


irgendwo die neue Funktion einfügen (ich hab sie direkt über der Nachrichtenschleife sollte aber egal sein)

Quellcode

1
2
3
4
5
6
7
8
9
// ******************************************************************
// Framebremse setzen
TRIBASE_API tbResult tbSetFramebrake(double dFramebrake)
{
    if(dFramebrake == 0.0) dFramebrake = 1000.0;
    tb_g_dFramebrake = 1.0 / dFramebrake;
    
    return TB_OK;
}


und die Nachrichtenschleife selbst ersetzen (bzw. umbenannt einfügen)

Quellcode

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// ******************************************************************
// Nachrichtenschleife
TRIBASE_API tbResult tbDoMessageLoop(tbResult (* pMoveProc)(float),
                                     tbResult (* pRenderProc)(float),
                                     double dMaxFPS)
{
    MSG         Message;        // Nachricht
    LONGLONG    llStartTime;    // Startzeitpunkt
    LONGLONG    llEndTime;      // Endzeitpunkt
    double      dTime;          // Zeitspanne in Sekunden
    BOOL        bQuit = FALSE;

    // Frameratenbremse setzen
    tbSetFramebrake(dMaxFPS);

    TB_INFO("Nachrichtenschleife wird betreten...");
    ZeroMemory(&Message, sizeof(MSG));

    // Startzeitpunkt vorab messen für die erste Schleife
    QueryPerformanceCounter((LARGE_INTEGER*)(&llStartTime));

    // Nachrichtenschleife
    dTime = 0.0001;
    while(!bQuit)
    {
        // Nachrichten verarbeiten
        while(PeekMessage(&Message, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&Message);
            DispatchMessage(&Message);

            if(Message.message == WM_QUIT)
            {
                // Schleife verlassen!
                bQuit = TRUE;
            }   
        }

        if(tb_g_bAppActive)
        {
            // Die Sekunden seit dem letzten Frame speichern
            tb_g_dNumSecsPassed = dTime;

            // Bewegungen durchführen, falls gewünscht
            if(pMoveProc != NULL) if(pMoveProc((float)(dTime)) == TB_STOP) break;

            
            // Frameratenzähler vor dem rendern midestens einmal durchlaufen und ggf. abbremsen
            do{
                // Endzeitpunkt messen und Anzahl der vergangenen Sekunden berechnen
                QueryPerformanceCounter((LARGE_INTEGER*)(&llEndTime));
                if(llEndTime == llStartTime) dTime = 0.0001;
                else dTime = (float)(llEndTime - llStartTime) / tb_g_dFrequency;
            }while(dTime < tb_g_dFramebrake);
            
            // Zeitzähler erhöhen
            tb_g_fTime += (float)(dTime);
            
            // Neuen Startzeitpunkt nach der Framebremse berechnen
            QueryPerformanceCounter((LARGE_INTEGER*)(&llStartTime));


            // Spielzustand zeichnen, falls gewünscht
            if(pRenderProc != NULL) if(pRenderProc((float)(dTime)) == TB_STOP) break;

            if(tbDirectSound::IsInitialized())
            {
                if(tbDirectSound::GetListener() != NULL)
                {
                    // Aufgeschobene 3D-Sound-Änderungen wirksam machen
                    tbDirectSound::GetListener()->CommitDeferredSettings();
                }
            }
        }
    }

    TB_INFO("Nachrichtenschleife beendet!");

    return TB_OK;

}


Das müsste es gewesen sein. Beten nicht vergessen! :)

Jetzt kann man beim Aufruf der tbDoMessageLoop() als dritten Parameter die maximale Framerate angeben. Tut man das nicht, läuft alles wie gehabt (hoffe ich zumindest :wirbel: ).

Das Kernstück ist ja die die do-while-Schleife

Quellcode

1
2
3
4
5
6
7
            // Frameratenzähler vor dem rendern midestens einmal durchlaufen und ggf. abbremsen
            do{
                // Endzeitpunkt messen und Anzahl der vergangenen Sekunden berechnen
                QueryPerformanceCounter((LARGE_INTEGER*)(&llEndTime));
                if(llEndTime == llStartTime) dTime = 0.0001;
                else dTime = (float)(llEndTime - llStartTime) / tb_g_dFrequency;
            }while(dTime < tb_g_dFramebrake);

Ich habe sie zwischen Berechnung und Rendern gepackt, weil es für mich logischer klang, denn wenn man eine Frameratenbremse einbaut, dann hat man (zumindest bei 2D) eine ziemlich konstante Rendermethode, so dass der variable Zeitfaktor in der Berechnung von Move liegt. Wenn die Berechnung also mal schneller fertig ist, dann soll sie halt vor dem rendern warten und berstimmt nicht danach.

Ok, das wärs dann...
...fürs erste ;)

Werbeanzeige