Trennen von Logik / Aktualisierung von Render- / Zeichencode in einem einzelnen Thread mithilfe des Ruhezustands

9

Ich habe gelesen, dass die Geschwindigkeit von Spielobjekten nicht durch FPS behindert werden sollte, sondern auf der Zeit basieren sollte. Wie kann ich den Aktualisierungs- / Zeichnungscode trennen, um die Leistung zu maximieren, ohne die Zeichnungsrate zu begrenzen, und eine konstante logische Aktualisierungsrate basierend auf der Zeit bereitstellen?

Mein aktueller Pseudocode lautet wie folgt

loop
{
    draw();
    if (ticksElapsed() > 100)
    {
        update();
        ticks+= ticksElapsed();
    }        
}

Das Problem ist, dass der Zeichnungscode die Leistung der update () -Rate beeinträchtigt. Und es verbraucht 100% CPU, denn wenn der Schlaf aktiviert wird, werden beide Zeichen- / Logikfunktionen deaktiviert.

Ich verwende auch SDL und es scheint keine vsync-Option zu geben. Ich habe auch von den Begriffen feste und variable Zeitschritte gehört, bin mir aber nicht sicher, wie das mit sleep gemacht werden kann ()

Oskenso Kashi
quelle
1
Sie müssen nicht 100% CPU-Leistung verschwenden, nur um zu warten. Setzen Sie am Ende der while-Schleife einen Ruhezustand (0), wenn ticksElapsed () <100 ist. Das Betriebssystem kehrt sofort zum Thread zurück, wenn kein anderer Thread vorhanden ist will rennen Aber keine 100% ige CPU-Leistung mehr verschwenden.
Maik Semder
Die beste Lösung für ein solches 1-Thread-Setup ist jedoch die Verwendung von vsync. Wenn Sie vsync nicht können, rufen Sie sleep (0) in einer Schleife auf, bis Sie die Zielbildrate erreicht haben, und aktualisieren und zeichnen Sie
Maik Semder

Antworten:

3

In Ihrem Code-Snippet sieht es so aus, als würden Sie versuchen, Ihr Spiel im Schrittmodus mit fester Zeit auszuführen, indem Sie warten, wenn das Zeichnen und Aktualisieren weniger als 15 ms (60 fps) gedauert hat. Dies ist möglich und Sie haben richtig geraten, dass dies nicht mit einem Schlafanruf möglich ist, da Sie nicht genau wissen, wie lange Sie schlafen gehen. Die Besetzt-Warteschleife ist die gute Lösung.

Betrachten Sie jedoch den Fall, in dem Ihre Aktualisierung und Zeichnung 15 ms überschreitet, haben Sie jetzt das Zeichnen und Aktualisieren des Spiels zu langsam. Sie können jetzt zwei Dinge tun: diesen Status erkennen und Frames löschen (Zeichnung überspringen und direkt mit der Aktualisierung fortfahren, bis Sie wieder synchron sind). Wenn der Computer jedoch nur zu langsam ist, holt er nie auf.

Eine andere Lösung besteht darin, Ihre Aktualisierungslogik zeitunabhängig zu machen. Sie benötigen dafür keinen separaten Thread, sondern müssen nur erneut festlegen, wie schnell sich die Dinge bewegen sollen. Anstelle von 5 Pixeln pro Tick sollten Sie 50 Pixel pro Sekunde verwenden. Sie benötigen einen hochpräzisen Timer, um dies zu erreichen, und Ihre gesamte Aktualisierungslogik sollte auf den Timer zugreifen können, um zu sehen, wie viel Zeit seit der letzten Aktualisierung vergangen ist.

Grundsätzlich gehen Sie von:

void UpdatePlayer()
 player.x += 10;

Zu

void UpdatePlayer(float elapsedSeconds) //the total seconds elapsed since last update
 player.x += walkspeed * elapsedSeconds;
Roy T.
quelle
Mein Motor verbraucht also immer 100% und ich kann nichts wirklich dagegen tun?
Oskenso Kashi
1
Es wird so viele Zyklen verbrauchen, wie der Scheduler zulässt, aber das ist kein wirkliches Problem, da Sie während eines Spiels nicht wirklich viele andere Dinge tun können :).
Roy T.
@Oskenso Es ist jedoch ein Problem , wenn Sie mehr als 1 Thread verwenden, dann der Haupt - Thread wird nicht zulassen , die andere laufen so viel wie sie konnten, viel Rechenleistung in der while - Schleife zu verschwenden, sollten Sie wirklich einen Schlaf betrachten
Maik Semder
@ Maik Semder: Haben Sie eine Lösung für Schlaf (x) nicht genau? Nach Ablauf des Ruheintervalls kann der Thread ausgeführt werden. Es ist jedoch nicht garantiert, dass ein fertiger Thread sofort ausgeführt wird. Das liegt beim Planer. Wenn Sie zwei Threads verwenden, gibt es andere Lösungen. Weitere Informationen
Roy T.
1
@ Roy Schlaf (0) ist die Lösung. Es wird sofort zurückgegeben, wenn kein anderer Thread ausgeführt werden soll ( Sleep WinAPI ), und gibt anderen Threads die Möglichkeit, ausgeführt zu werden. Wenn der andere Thread dem Haupt-Thread nicht die Möglichkeit gibt, im Gegenzug ausgeführt zu werden, liegt ein Threading-Problem vor. Wenn Sie jedoch alles andere blockieren, indem Sie nicht zuerst den Schlaf aufrufen, wird dies noch schlimmer und ist kaum eine Lösung. Der Schlüssel ist, den Ruhezustand (0) aufzurufen und die verstrichene Zeit zu testen, bis Sie Ihre feste Zielbildrate erreicht haben, damit Sie nicht 100% CPU nur zum Warten verschwenden.
Maik Semder