Verwenden der Leerlaufzeit in rundenbasierten Spielen (RPG) zum Aktualisieren

9

Wenn Sie ein rundenbasiertes RPG-Spiel spielen, wird es große Zeiträume geben, in denen nichts passiert, da das Spiel über 'wait_for_player_input' läuft. Natürlich erscheint es sinnvoll, diese Zeit zu nutzen, um Dinge zu aktualisieren.

Dies scheint jedoch sofort darauf hinzudeuten, dass es eingefädelt werden müsste. Ist diese Art von Design in einem einzigen Thread möglich?

loop:  
if not check_something_pressed:  
    update_a_very_small_amount  
else  
  keep going

Wenn wir jedoch sagen, dass 'a_very_small_amount' nur ein einzelnes Objekt in jeder Schleife aktualisiert, wird die Aktualisierung sehr langsam sein.

Wie würden Sie vorgehen, vorzugsweise in einem einzigen Thread?

EDIT: Ich habe diesen sprachunabhängigen Tag markiert, da dies vernünftig erscheint, obwohl alles, was spezifischer für Python ist, großartig wäre. ;-);

Zweite Bearbeitung: Dieses Spiel plant keine animierten Komponenten. Das heißt, ich führe es derzeit als Warte-auf-Spieler-Eingabe aus, aktualisiere dann alles und zeichne. Anstelle von X FPS hängt es also von der Geschwindigkeit des Benutzers ab.

Die kommunistische Ente
quelle

Antworten:

7

Ja, es ist möglich, in einem einzigen Thread zu tun. Im Allgemeinen sollten Sie die Objekte jedoch in jedem Frame aktualisieren und nicht nur, wenn Ersatzzyklen vorhanden sind. Ihre Animationen und Bewegungen werden von der Bildrate getrennt und sehen ziemlich abgehackt aus, wenn Sie dies nicht tun. Wenn Sie mehr über KI-Updates oder etwas anderes sprechen, das nicht in Echtzeit erfolgen muss, würde ich einen Timer darauf setzen. Sie sollten wissen, wie hoch Ihre Zielbildrate ist, und die Leerlaufzeit ist die verbleibende Zeit, nachdem alles andere abgeschlossen wurde.

Angenommen, Sie zielen auf 60 FPS für Ihr Spiel ab. Damit haben Sie 16,667 ms Zeit, um alle Arbeiten auszuführen, die Sie für jeden Frame benötigen. Holen Sie sich zu Beginn des Spiels die aktuelle Zeit mit dem Timer mit der höchsten verfügbaren Auflösung, fügen Sie 16,667 ms hinzu und speichern Sie sie irgendwo. Ich denke, die Funktion in Python ist time (), obwohl es eine Weile her ist, seit ich in der Sprache gearbeitet habe. Geben Sie nach Abschluss Ihrer Verarbeitung eine Schleife ein, die die aktuelle Zeit mit der von Ihnen aufgezeichneten Zeit vergleicht. Wenn die aktuelle Zeit kürzer als die Frame-Endzeit ist, aktualisieren Sie_a_very_small_amount. Ich würde mir keine Sorgen machen, dass die Verarbeitung über das Ende des Frames hinausgeht, da Ihr kleines Update schnell zu verarbeiten sein sollte. Es wäre nur eine geringfügige Verzögerung bis zum Start des nächsten Frames, und Sie scheinen genügend Leerlaufzeit zu haben, um damit umzugehen.

Nachdem der Frame die Verarbeitung abgeschlossen hat, addieren Sie 16,667 ms zu der Zeit, die für das Ende des letzten Frames gespeichert wurde, um herauszufinden, wo das Ende des nächsten Frames sein sollte. Wenn Sie die aktuelle Zeit + 16.667 ms verwenden und die Verarbeitung beendet ist, wird das Ende des nächsten Frames um die Zeit verschoben, die der letzte Frame überfahren hat.

Betreff: Zweite Bearbeitung

Zur Verdeutlichung verwende ich hier den Begriff Framerate, um eine Iteration durch die Hauptschleife anzuzeigen. Wenn es auf der Eingabegeschwindigkeit des Benutzers basiert, stelle ich mir vor, dass Ihr Ziel darin besteht, das Spiel einfach reaktionsschnell zu machen. Andernfalls könnten Sie einfach jedes Mal durch die Schleife nach Eingaben suchen und alles aktualisieren, selbst wenn dies 10 Sekunden dauert. Damit es sich jedoch reaktionsschnell anfühlt, sollten Sie wahrscheinlich etwa 20 Mal pro Sekunde nach Eingaben suchen, was eine effektive Bildrate von 20 FPS ergibt, selbst wenn Sie diese Bilder nicht tatsächlich zeichnen. Dies würde Ihnen 50 ms Zeit geben, um die Dinge zu aktualisieren, bevor Sie erneut nach Eingaben suchen müssen.

Joel Baker
quelle
2

Speziell für Python können Sie versuchen, Coroutines zu verwenden, um Berechnungen über mehrere Update-Aufrufe durchzuführen .

... Coroutinen sind Programmkomponenten, die Unterprogramme verallgemeinern, um mehrere Einstiegspunkte zum Anhalten und Fortsetzen der Ausführung an bestimmten Stellen zu ermöglichen ...

PEP-0342 beschreibt die Coroutine-Implementierung in Python.

Sie können Ihren eigenen Scheduler und Aufgaben erstellen, die Multithreading in einem einzelnen Thread simulieren können. Dies ist die gleiche Art und Weise, wie Javascript-Frameworks eine Multithread-ähnliche Verarbeitung ermöglichen, obwohl Javascript in einem einzigen Prozess ausgeführt wird.

David Young
quelle
1

Ja das ist möglich. Ihr Spiel wird eine Art Hauptspielschleife haben. Etwas wie das:

while(gameRunning)
{
  checkUserInput()
  updateGame()
  renderGame()
}

In updateGame können Sie Ihre Spielobjekte durchlaufen und "ein wenig" aktualisieren. Wenn Sie mit dieser Methode umfangreiche Berechnungen durchführen, bleibt das Spiel einfach hängen. Sie müssen also eine Möglichkeit haben, diese Berechnungen aufzuteilen, um mehrere Iterationen der Spielschleife zu durchlaufen.

Wie Sie sie aufteilen, hängt von der Art des Spiels ab, das Sie erstellen. Angenommen, Sie haben eine Pfadfinder-Routine, die A * verwendet, um einen Pfad durch ein Labyrinth zu berechnen. Sie müssten den Algorithmus nach einer bestimmten Zeit oder nach einer festgelegten Anzahl von Iterationen stoppen, den berechneten Pfad so weit beibehalten und die Kontrolle wieder in die Spielschleife zurückführen. Die Pfadfindung wird beim nächsten Aufruf von updateGame fortgesetzt. Sie können das Zeichen sogar entlang des Teilpfads verschieben, während es noch berechnet wird.

In den meisten Fällen müssen Sie sich keine Sorgen machen, dass der UpdateGame-Aufruf zu lange dauert.

"Vorzeitige Optimierung ist die Wurzel allen Übels!"
Wenn Sie in Ihrer Update-Routine auf einen Leistungsengpass stoßen, ist es an der Zeit, dies zu untersuchen und zu optimieren. Da Sie nicht im Voraus wissen können, welche Teile Ihres Spiels die meiste Zeit zum Berechnen benötigen und möglicherweise Zeit damit verschwenden, die falschen Dinge zu optimieren.

Stephen
quelle
1

Ich verstehe Ihre Frage so, dass Sie im Grunde nach kooperativem Multitasking fragen.

Die Basisinfrastruktur wäre eine Liste von Funktionszeigern, die alle im Round-Robin-Verfahren aufgerufen werden (es sei denn, eine differenziertere Priorisierung ist erforderlich), und alle diese Funktionen würden "ein wenig Arbeit" leisten, klein genug, um das Spiel nicht zu veranlassen träge werden. Ich habe dies unter DOS getan, bevor Threads heiß waren, und auch auf Mobilgeräten, die kein Threading unterstützten.

Eine bessere Frage ist meiner Meinung nach, was zu tun ist, während darauf gewartet wird, dass sich der Spieler bewegt. Welche Art von Dingen wäre so prozessorintensiv, dass sie ohnehin nicht im laufenden Betrieb ausgeführt werden können, während sie gleichzeitig so optional sind, dass das Spiel keine Spieltaste schnell genug hämmert, was das Spiel nicht hat Zeit, diese zu verarbeiten. Dinge wie die Berechnung von A * für ein paar tausend AIs sind also aus.

Eine bessere Lösung ist meiner Meinung nach, einfach nachzugeben / zu schlafen und das Power Management des Computers seine Arbeit machen zu lassen. Laptop-Benutzer werden Sie dafür besser mögen.

Jari Komppa
quelle