2009-08-08 8 views
16

Ich habe gehört, dass Koroutinen eine gute Möglichkeit sind, Spiele zu strukturieren (zB PEP 342: "Coroutines sind eine natürliche Art, viele Algorithmen auszudrücken, wie Simulationen, Spiele ..."), aber ich habe eine harte Zeit Verpackung mein Kopf herum wie das eigentlich gemacht würde.Coroutinen für das Spieldesign?

Ich sehe von diesem article, dass Koroutinen Zustände in einer Zustandsmaschine darstellen können, die mit einem Scheduler ineinander übergehen, aber es ist mir nicht klar, wie dies für ein Spiel gilt, wo sich der Spielzustand basierend auf Bewegungen von mehreren ändert Spieler.

Gibt es ein einfaches Beispiel für ein Spiel, das mit Coroutinen geschrieben wurde? Oder kann jemand eine Skizze anbieten, wie es gemacht werden könnte?

Antwort

7

One-Way-Coroutinen können in Spielen verwendet werden, wie leichte Fäden in einem Schauspieler wie Modell, wie in Kamaelia.

Jedes Objekt in Ihrem Spiel wäre eine Kamaelia "Komponente". Eine Komponente ist ein Objekt, das die Ausführung pausieren kann, indem es nachgibt, wenn es zulässig ist, zu pausieren. Diese Komponenten verfügen außerdem über ein Messaging-System, mit dem sie sicher asynchron miteinander kommunizieren können.

Alle Objekte würden gleichzeitig ihr eigenes Ding machen, wobei Nachrichten gesendet werden, wenn Interaktionen stattfinden.

Also, es ist nicht wirklich spezifisch für Spiele, aber alles, wenn Sie eine Vielzahl von gleichzeitig kommunizierenden kommunizierenden Komponenten haben, könnte von Koroutinen profitieren.

9

Koroutinen ermöglichen die Schaffung große Mengen an sehr leichten „Microthreads "Mit kooperativem Multitasking (dh Mikrothreads, die sich absichtlich gegenseitig aussetzen, um andere Mikrothreads laufen zu lassen). Lesen Sie in Dave Beazleys article zu diesem Thema.

Jetzt ist es offensichtlich, wie solche Mikrothreads für die Spielprogrammierung nützlich sein können. Stellen Sie sich ein Echtzeit-Strategiespiel vor, in dem Sie Dutzende von Einheiten haben - jede mit einem eigenen Kopf. Es kann eine bequeme Abstraktion für die KI jeder Einheit sein, um als ein solcher Mikrothread in Ihrer simulierten Multitasking-Umgebung zu laufen. Dies ist nur ein Beispiel, ich bin mir sicher, dass es mehr gibt.

Die "Coroutine Spieleprogrammierung" Suche bei Google scheint interessante Ergebnisse zu bringen.

8

Der prominenteste Fall von Koroutinen sind wahrscheinlich alte grafische Point & klicken Sie auf Adventure-Spiele, wo sie zum Erstellen von Zwischensequenzen und andere animierte Sequenzen im Spiel verwendet wurden. Ein einfacher Code-Beispiel würde wie folgt aussehen:

# script_file.scr 
bob.walkto(jane) 
bob.lookat(jane) 
bob.say("How are you?") 
wait(2) 
jane.say("Fine") 
... 

Diese ganze Sequenz nicht als normaler Code geschrieben werden kann, wie Sie bob tun, um seine Wanderung Animation sehen möchten, nachdem Sie bob.walkto(jane) statt springen direkt in die nächste Zeile tat . Für die Walk-Animation, die Sie spielen, müssen Sie jedoch die Kontrolle über die Game-Engine zurückgeben, und das ist der Punkt, an dem Korotinen ins Spiel kommen.

Diese ganze Sequenz wird als Coroutine ausgeführt, was bedeutet, dass Sie die Funktion beliebig anhalten und fortsetzen können.Ein Befehl wie bob.walkto(jane) teilt dem motorseitigen Bob-Objekt sein Ziel mit und unterbricht dann die Coroutine und wartet auf einen Weckruf, wenn Bob sein Ziel erreicht hat.

auf der Motorseite Dinge könnte wie dieser (Pseudo-Code) aussehen:

class Script: 
    def __init__(self, filename): 
     self.coroutine = Coroutine(filename) 
     self.is_wokenup = True 

    def wakeup(self): 
     self.is_wokenup = False; 

    def update(): 
     if self.is_wokenup: 
      coroutine.run()    


class Character: 
    def walkto(self, target, script): 
     self.script = script 
     self.target = target 

    def update(self): 
     if target: 
      move_one_step_closer_to(self.target) 
      if close_enough_to(self.target): 
       self.script.wakeup() 

       self.target = None 
       self.script = None 

objects = [Character("bob"), Character("jane")] 
scripts = [Script("script_file.scr")] 

while True: 
    for obj in objects: 
     obj.update() 

    for scr in scripts: 
     scr.update() 

Ein litte Wort der Warnung jedoch während Koroutinen Codierung dieser Sequenzen sehr einfach zu machen, nicht alle Implementierungen Sie von ihnen finden wird Wenn Sie Serialisierung im Hinterkopf haben, wird das Speichern von Spielen zu einem ziemlich lästigen Problem, wenn Sie Coroutinen intensiv nutzen.

Dieses Beispiel ist auch nur der einfachste Fall einer Coroutine in einem Spiel, Coroutinen selbst können für viele andere Aufgaben und Algorithmen verwendet werden.

1

Es ist durchaus üblich, Koroutinen zu verwenden, um einzelne KI-Skripte darzustellen. Leider neigen die Leute dazu zu vergessen, dass Coroutinen alle die gleichen Synchronisations- und gegenseitigen Ausschlussprobleme haben wie Threads, nur auf einem höheren Level. Daher müssen Sie oft zuerst so viel wie möglich tun, um den lokalen Zustand zu eliminieren, und zweitens robuste Methoden schreiben, um die Fehler in einer Coroutine zu behandeln, die entstehen, wenn etwas, auf das Sie sich beziehen, nicht mehr existiert.

In der Praxis sind sie ziemlich schwer zu nutzen, weshalb Sprachen wie UnrealScript, die einen gewissen Anschein von Koroutinen verwenden, einen Großteil der nützlichen Logik für atomare Ereignisse verwenden. Manche Leute bekommen einen guten Nutzen aus ihnen, z. die EVE Online-Leute, aber sie mussten ihr gesamtes System ziemlich um das Konzept herum entwerfen. (Und wie sie Konflikte über freigegebene Ressourcen behandeln, ist nicht gut dokumentiert.)

+0

Coroutines haben einige Synchronisierungsprobleme, aber nur, wenn ihr Zustand zwischen yield() -Aufrufen nicht konsistent sein kann. Eine implizite Sperre für den gesamten Code zwischen yield() -Aufrufen kann die Dinge erheblich vereinfachen. Der größte Fehler ist, dass bei jeder Erstellung einer Routine entschieden werden sollte, ob die Routine immer abgeschlossen werden kann, bevor sie ausgeführt werden muss(). Wenn der Aufrufer einer Routine nicht erwartet, dass eine Routine Ergebnisse liefert, befinden sich die Objekte möglicherweise nicht in einem konsistenten Zustand, wenn die Routine aufgerufen wird. Das Hinzufügen einer Rendite() wäre eine bahnbrechende Änderung. – supercat

+0

Das Problem ist, dass Sie in der Regel in der Lage sein können, für solche Routinen jeden Nutzen zu erbringen (wie in UnrealScript, wo "latente Funktionen" implizit ergeben), aber dies führt zu Kombinationen von Koroutinen, die auf unvorhersehbare Weise interagieren. z.B. Wenn man eine Patrouillen-Koroutine ist, die sagt: "Geh nach Norden; sieh nach; geh nach Süden; schau; wiederhole;" und die andere ist eine Reaktionsroutine, die sagt: "Wenn (Ton gehört in den Westen) {gehe nach Westen; schau; gehe nach Osten;}", dann könnte die Verschachtelung der beiden dazu führen, dass sich der Schauspieler eher unvorhersehbar bewegt. Deshalb erlaubt UnrealScript solche Funktionen nur in genau einer "Coroutine". – Kylotan

+0

Und selbst wenn Sie nur 1 Koroutine pro Akteur haben, reicht das immer noch nicht aus, um sicherzustellen, dass die Logik immer auf der Sprachebene funktioniert: wenn Ihre Koroutine sagt: "Schwert ziehen; 1 Sekunde warten; Gegner mit Schwert angreifen" dann passiert, wenn das Schwert verschwindet, weil du von einem anderen Spieler entwaffnet wurdest? Insbesondere, wenn Ihre Sprache diese Coroutine implementiert, indem Sie eine lokale Variable für ein Schwert verwenden, das sich jetzt auf ein Null-Objekt bezieht? Es ist sehr schwierig, all diese Fälle zu berücksichtigen, wenn Sie eine allgemeine Sprache in der üblichen Weise verwenden. – Kylotan