Ich fange gerade an, OpenGL in der Schule zu lernen, und ich habe neulich angefangen, ein einfaches Spiel zu machen (allein, nicht für die Schule). Ich verwende freeglut und erstelle es in C, also habe ich für meine Spieleschleife wirklich gerade eine Funktion verwendet, an die ich mich gewöhnt habe, glutIdleFunc
um die gesamte Zeichnung und Physik in einem Durchgang zu aktualisieren. Dies war in Ordnung für einfache Animationen, bei denen mir die Framerate nicht so wichtig war, aber da das Spiel hauptsächlich auf Physik basiert, möchte ich wirklich festhalten, wie schnell es aktualisiert wird (müssen).
Daher bestand mein erster Versuch darin, meine Funktion an glutIdleFunc
( myIdle()
) zu übergeben, um zu verfolgen, wie viel Zeit seit dem vorherigen Aufruf vergangen ist, und die Physik (und derzeit die Grafiken) alle so viele Millisekunden zu aktualisieren. Früher timeGetTime()
habe ich das gemacht (mit <windows.h>
). Und das brachte mich zum Nachdenken: Ist die Verwendung der Leerlauffunktion wirklich eine gute Methode, um die Spielschleife zu durchlaufen?
Meine Frage ist, was ist ein besserer Weg, um die Spieleschleife in OpenGL zu implementieren? Sollte ich es vermeiden, die Leerlauffunktion zu verwenden?
Antworten:
Die einfache Antwort lautet: Nein, Sie möchten den glutIdleFunc-Rückruf nicht in einem Spiel verwenden, das eine Art Simulation enthält. Der Grund dafür ist, dass diese Funktion die Animation trennt und Code aus der Fensterereignisbehandlung zeichnet, jedoch nicht asynchron. Mit anderen Worten, das Empfangen und Übergeben von Fensterereignissen, bei denen Stalls Code zeichnen (oder was auch immer Sie in diesen Rückruf einfügen), ist für eine interaktive Anwendung (Interaktion, dann Antwort) vollkommen in Ordnung, jedoch nicht für ein Spiel, bei dem die Physik oder der Spielstatus fortschreiten müssen unabhängig von Interaktion oder Renderzeit.
Sie möchten das Eingabehandling, den Spielstatus und den Zeichencode vollständig entkoppeln. Es gibt eine einfache und übersichtliche Lösung, bei der die Grafikbibliothek nicht direkt einbezogen wird (dh sie ist portabel und einfach zu visualisieren). Sie möchten, dass die gesamte Spielschleife Zeit produziert und die Simulation die produzierte Zeit (in Stücken) verbraucht. Der Schlüssel ist jedoch, die Zeit, die Ihre Simulation benötigt, in Ihre Animation zu integrieren.
Die beste Erklärung und das beste Tutorial, das ich dazu gefunden habe, ist Glenn Fiedlers Fix Your Timestep
Dieses Tutorial hat die vollständige Beschreibung. Wenn Sie jedoch keine tatsächliche Physiksimulation haben, können Sie die eigentliche Integration überspringen, aber die grundlegende Schleife läuft immer noch auf (in ausführlichem Pseudocode):
Wenn Sie dies auf diese Weise tun, führen Stalls in Ihrem Rendercode, der Eingabeverarbeitung oder dem Betriebssystem nicht dazu, dass Ihr Spielstatus zurückfällt. Diese Methode ist auch portabel und unabhängig von der Grafikbibliothek.
GLUT ist eine gute Bibliothek, jedoch streng ereignisgesteuert. Sie registrieren Rückrufe und lösen die Hauptschleife aus. Sie geben die Kontrolle über Ihre Hauptschleife immer mit GLUT ab. Es gibt Hacks, um das zu umgehen , Sie können auch eine externe Schleife mit Timern und dergleichen vortäuschen, aber eine andere Bibliothek ist wahrscheinlich der bessere (einfachere) Weg. Es gibt viele Alternativen, hier einige (mit guter Dokumentation und kurzen Anleitungen):
quelle
Ich denke,
glutIdleFunc
ist gut so; Es ist im Wesentlichen das, was Sie am Ende sowieso von Hand tun würden, wenn Sie es nicht verwenden würden. Egal, was Sie haben werden, eine enge Schleife, die nicht in einem festen Muster aufgerufen wird, und Sie müssen die Wahl treffen, ob Sie sie in eine Schleife mit fester Zeit umwandeln oder eine Schleife mit variabler Zeit beibehalten und ausführen möchten Stellen Sie sicher, dass alle Ihre mathematischen Berechnungen die Interpolationszeit berücksichtigen. Oder auch irgendwie eine Mischung daraus.Sie können sicherlich eine
myIdle
Funktion haben, an die Sie übergebenglutIdleFunc
, und innerhalb dieser Funktion die verstrichene Zeit messen, durch Ihren festgelegten Zeitschritt dividieren und eine andere Funktion so oft aufrufen.Folgendes sollten Sie nicht tun:
fixedTimestep würde nicht alle 10 Millisekunden aufgerufen. Tatsächlich würde es langsamer als in Echtzeit ablaufen, und Sie könnten am Ende falsche Zahlen erhalten, um zu kompensieren, wenn die eigentliche Wurzel des Problems im Verlust des Restbetrags liegt, wenn Sie
timeDifference
durch 10 dividieren .Tun Sie stattdessen Folgendes:
Diese Schleifenstruktur stellt nun sicher, dass Ihre
fixedTimestep
Funktion einmal pro 10 Millisekunden aufgerufen wird, ohne dass Millisekunden als Rest oder Ähnliches verloren gehen.Aufgrund des Multitasking-Charakters von Betriebssystemen können Sie sich nicht darauf verlassen, dass eine Funktion genau alle 10 Millisekunden aufgerufen wird. Die obige Schleife würde jedoch schnell genug ablaufen, so dass sie hoffentlich eng genug wäre, und Sie können davon ausgehen, dass jeder Aufruf von
fixedTimestep
Ihre Simulation um 10 Millisekunden inkrementieren würde.Bitte lesen Sie auch diese Antwort und insbesondere die in der Antwort enthaltenen Links.
quelle