Ich habe einen ausführlichen Artikel von Koen Witters über verschiedene Spiele-Loop-Lösungen gelesen, aber ich habe welche Probleme beim Implementieren des letzten mit GLUT, welches der empfohlene ist.
Nachdem ich ein paar Artikel, Tutorials und Code von anderen Leuten darüber gelesen habe, wie man eine konstante Spielgeschwindigkeit erreicht, denke ich, dass was ich zur Zeit implementiert habe (ich poste den Code unten), was Koen Witters nannte Game Speed abhängig von Variable FPS , der zweite in seinem Artikel.
Erstens, durch meine suchende Erfahrung, gibt es ein paar Leute, die wahrscheinlich das Wissen haben, um darüber zu helfen, aber nicht wissen, was GLUT ist und ich werde versuchen und erklären (fühlen Sie sich frei, mich zu korrigieren) relevante Funktionen für mein Problem mit diesem OpenGL-Toolkit. Überspringen Sie diesen Abschnitt, wenn Sie wissen, was GLUT ist und wie man damit spielt.
GLUT-Toolkit:
glutDisplayFunc(renderScene)
nimmt einen Zeiger auf einen renderScene()
-Funktionsrückruf, der für das Rendern von allem verantwortlich ist. Die Funktion renderScene()
wird nur einmal nach der Rückrufregistrierung aufgerufen. glutTimerFunc(TIMER_MILLISECONDS, processAnimationTimer, 0)
aufgerufen wird. Das letzte Argument ist nur ein Wert, der an den Timer-Callback übergeben wird. Der processAnimationTimer()
wird nicht pro processAnimationTimer()
, sondern nur einmal aufgerufen. TIMER_MILLISECONDS
fordert GLUT dazu auf, einen neuen Frame zu rendern, so dass wir ihn jedes Mal aufrufen müssen, wenn wir etwas in der Szene ändern. glutPostRedisplay()
könnte verwendet werden, um einen Rückruf auf glutIdleFunc(renderScene)
zu registrieren (dies macht renderScene()
nicht relevant), aber diese Funktion sollte vermieden werden, da der Leerlauf-Callback kontinuierlich aufgerufen wird, wenn keine Ereignisse empfangen werden CPU-Auslastung. glutDisplayFunc()
gibt die Anzahl der Millisekunden zurück, seit glutGet(GLUT_ELAPSED_TIME)
aufgerufen wurde (oder der erste Aufruf von glutInit
). Das ist der Timer, den wir mit GLUT haben. Ich weiß, dass es bessere Alternativen für hochauflösende Timer gibt, aber bleiben wir jetzt bei dieser Lösung. Ich denke, das ist genug Information darüber, wie GLUT Frames liefert, so dass Leute, die nichts davon wussten, auch in dieser Frage versuchen könnten, zu helfen, wenn sie so wären.
Aktuelle Implementierung:
Nun, ich bin mir nicht sicher, ob ich die zweite von Koen vorgeschlagene Lösung korrekt implementiert habe, Game Speed abhängig von Variable FPS . Der entsprechende Code dafür lautet:
%Vor% Diese Implementierung stimmt nicht. Es funktioniert in dem Sinne, dass die Spielgeschwindigkeit abhängig vom FPS konstant ist. Der Übergang von Punkt A nach Punkt B dauert unabhängig von der hohen / niedrigen Framerate gleich lang. Ich glaube jedoch, dass ich die Spielfrequenz bei diesem Ansatz einschränke. [ BEARBEITEN: Jeder Frame wird nur gerendert, wenn der Time Callback aufgerufen wird. Das bedeutet, dass die Framerate ungefähr um glutGet(GLUT_ELAPSED_TIME)
Frames pro Sekunde liegt. Das fühlt sich nicht richtig an, du solltest deine leistungsstarke Hardware nicht einschränken, es ist falsch. Es ist jedoch mein Verständnis, dass ich immer noch TICKS_PER_SECOND
berechnen muss. Nur weil ich GLUT sage, den Timer-Callback alle elapsedTime
aufzurufen, heißt das nicht, dass es das immer rechtzeitig tun wird.]
Ich bin nicht sicher, wie ich das beheben kann und um ganz ehrlich zu sein, ich habe keine Ahnung, was ist die Spielschleife in GLUT, wissen Sie, die TIMER_MILLISECONDS
Schleife in Koens Artikel. [ BEARBEITEN: Ich verstehe, dass GLUT ereignisgesteuert ist und diese Spielschleife beginnt, wenn ich while( game_is_running )
aufrufe (was niemals zurückkehrt), ja?]
Ich dachte, ich könnte einen Leerlauf-Callback mit glutMainLoop()
registrieren und diesen als Ersatz für glutIdleFunc()
verwenden, nur Rendern bei Bedarf (statt wie immer), aber wenn ich dies mit einem leeren Callback getestet habe (wie glutTimerFunc()
) und es war im Grunde nichts, nur ein schwarzer Bildschirm, die CPU stach zu 25% und blieb dort, bis ich das Spiel tötete und es normalisierte. Ich denke nicht, dass das der Weg ist, dem man folgen muss.
Die Verwendung von void gameLoop() {}
ist definitiv kein guter Ansatz, um alle darauf basierenden Bewegungen / Animationen auszuführen, da ich mein Spiel auf einen konstanten FPS beschränke, nicht cool. Oder vielleicht benutze ich es falsch und meine Implementierung ist nicht richtig?
Wie genau kann ich mit variablem FPS eine konstante Spielgeschwindigkeit haben? Genauer gesagt, wie setze ich Koen's konstante Spielgeschwindigkeit mit maximaler FPS Lösung (die vierte in seinem Artikel) richtig mit GLUT um? Vielleicht ist das mit GLUT überhaupt nicht möglich? Wenn nicht, was sind meine Alternativen? Was ist der beste Ansatz für dieses Problem (konstante Spielgeschwindigkeit) mit GLUT?
[EDIT] Ein anderer Ansatz:
Ich habe experimentiert und hier ist, was ich jetzt erreichen konnte. Anstatt die verstrichene Zeit für eine zeitgesteuerte Funktion zu berechnen (die die Framerate meines Spiels begrenzt), mache ich das jetzt in glutTimerFunc()
.Wenn Änderungen an der Szene passieren, rufe ich renderScene()
auf (dh: Kamera bewegt sich, einige Objektanimationen, etc ...), die einen Aufruf an glutPostRedisplay()
machen. Ich kann die abgelaufene Zeit in dieser Funktion verwenden, um beispielsweise meine Kamera zu bewegen.
Mein Code ist jetzt in folgendes umgewandelt:
%Vor%Fazit, es funktioniert, oder so scheint es. Wenn ich die Kamera nicht bewege, ist die CPU-Auslastung niedrig, es wird nichts gerendert (zu Testzwecken habe ich nur ein Raster für 4000.0f, während zFar auf 1000.0f gesetzt ist). Wenn ich anfange, die Kamera zu bewegen, fängt die Szene an, sich neu zu zeichnen. Wenn ich die Bewegungstasten drücke, erhöht sich die CPU-Auslastung; Das ist normales Verhalten. Es fällt zurück, wenn ich aufhöre, mich zu bewegen.
Wenn ich etwas nicht verpasse, scheint es für jetzt ein guter Ansatz zu sein. Ich habe diesen interessanten Artikel zu iDevGames gefunden und diese Implementierung wird wahrscheinlich von dem in diesem Artikel beschriebenen Problem beeinflusst. Was denkst du darüber?
Bitte beachtet, dass ich das nur zum Spaß tue, ich habe nicht die Absicht, ein Spiel zu erstellen, um es zu verteilen oder so etwas, zumindest nicht in naher Zukunft. Wenn ich das täte, würde ich wahrscheinlich mit etwas anderem außer GLUT gehen. Aber, da ich GLUT verwende, und anders als das Problem, das auf iDevGames beschrieben wird, denken Sie, dass diese neueste Implementierung für GLUT ausreichend ist? Das einzige wirkliche Problem, das mir gerade einfällt, ist, dass ich jedes Mal renderScene()
aufrufen muss, wenn die Szene etwas ändert, und es weiter anruft, bis nichts Neues neu zu zeichnen ist. Eine kleine Kompliziertheit, die dem Code für eine bessere Ursache hinzugefügt wird, denke ich.
Was denkst du?
glut ist so konzipiert, dass es die Spieleschleife sein ist. Wenn Sie glutMainLoop () aufrufen, führt es eine 'for-Schleife' mit keiner Beendigungsbedingung außer dem exit () - Signal aus. Sie können Ihr Programm so implementieren, wie Sie es jetzt tun, aber Sie benötigen einige kleinere Änderungen. Wenn Sie zuerst wissen möchten, was der FPS ist, sollten Sie dieses Tracking in die Funktion renderScene () einfügen, nicht in Ihre Update-Funktion. Natürlich wird Ihre Update-Funktion so schnell aufgerufen, wie vom Timer angegeben, und Sie behandeln elapsedTime als Maß für die Zeit zwischen den Frames. Im Allgemeinen wird das wahr sein, weil Sie glutPostRedisplay eher langsam aufrufen und Glut wird nicht versuchen, den Bildschirm zu aktualisieren, wenn es nicht nötig ist (es gibt keinen Grund neu zu zeichnen, wenn sich die Szene nicht geändert hat). Es gibt jedoch auch andere Zeiten, in denen renderScene aufgerufen wird. Zum Beispiel, wenn Sie etwas über das Fenster ziehen. Wenn Sie das tun würden, würden Sie einen höheren FPS sehen (wenn Sie den FPS in der Renderfunktion richtig verfolgt hätten).
Sie könnten glutIdleFunc
verwenden, das fortlaufend aufgerufen wird, wenn möglich - ähnlich der while(game_is_running)
-Schleife. Das heißt, welche Logik Sie sonst in diese while
-Schleife legen würden, könnten Sie in den Rückruf für glutIdleFunc
einfügen. Sie können vermeiden, glutTimerFunc
zu verwenden, indem Sie die Ticks selbst verfolgen, wie in dem Artikel, den Sie verlinkt haben (mit glutGet(GLUT_ELAPSED_TIME)
).
Als Beispiel haben Sie eine mausgesteuerte Rotationsmatrix, die unabhängig von der Rendering-Bildrate mit einer festen Bildrate aktualisiert wird. In meinem Programm schaltet die Leertaste den Benchmark-Modus um und bestimmt das boolesche fxFPS.
Lassen Sie die Maustaste los, während Sie ziehen, und Sie können ein Objekt, das von dieser Matrix transformiert wurde, "werfen".
Wenn fxFPS wahr ist, wird die Rendering-Bildrate auf die Animationsbildfrequenz gedrosselt; ansonsten werden identische Frames für das Benchmarking wiederholt gezeichnet, obwohl nicht genug Millisekunden vergangen sind, um eine Animation auszulösen.
Wenn Sie daran denken, Frames zu VERRINGERN und zu beschleunigen, müssen Sie genau überlegen, ob Sie Rendering- oder Animationsframes in jedem Fall meinen. In diesem Beispiel wird die Renderdrosselung für einfache Animationen mit der Animationsbeschleunigung kombiniert, wenn Frames in einer möglicherweise langsamen Animation gelöscht werden sollen.
Um die Animation zu beschleunigen, werden Rotationen wiederholt in einer Schleife ausgeführt. Solch eine Schleife ist nicht zu langsam im Vergleich mit der Option, trig mit einem adaptiven Drehwinkel zu machen; Sei nur vorsichtig, was du in eine Schleife legst, die tatsächlich länger dauert, je niedriger der FPS. Diese Schleife benötigt weit weniger als einen zusätzlichen Frame für jeden Frame-Drop, den sie berücksichtigt, so dass sie einigermaßen sicher ist.
%Vor%Und später ...
%Vor%Viel Spaß beim Werfen von Dingen um eine Achse; Ich finde, dass die meisten Leute das tun. Beachten Sie, dass die fps in der Benutzeroberfläche oder beim Rendern keinerlei Auswirkungen haben. Ich habe die Verwendung von Divisionen minimiert, daher sollten Vergleiche nett und genau sein, und jede Ungenauigkeit in der Uhr wird nicht unnötig angehäuft.
Die Synchronisierung von Multiplayer-Spielen ist weitere 18 Gespräche, würde ich beurteilen.