Ich habe Probleme damit, Skripte in meiner Spiel-Engine zu implementieren. Ich habe nur ein paar Anforderungen: Es sollte intuitiv sein, ich möchte keine benutzerdefinierte Sprache, keinen Parser und keinen Interpreter schreiben und ich möchte kein Threading verwenden. (Ich bin mir sicher, dass es eine einfachere Lösung gibt. Ich brauche nicht den Aufwand mehrerer Game Logic-Threads.) Hier ist ein Beispielskript in Python (auch bekannt als Pseudocode):
def dramatic_scene(actors):
alice = actors["alice"]
bob = actors["bob"]
alice.walk_to(bob)
if bob.can_see(alice):
bob.say("Hello again!")
else:
alice.say("Excuse me, Bob?")
Dieses epische Stück Storytelling wirft Implementierungsprobleme auf. Ich kann nicht einfach die ganze Methode auf einmal bewerten, weilwalk_to
das Spiel Zeit kostet. Wenn es sofort zurückkommt, würde Alice auf Bob zugehen und (im selben Frame) Hallo sagen (oder begrüßt werden). Aber wenn walk_to
es sich um einen Blockierungsaufruf handelt, der zurückkommt, wenn sie Bob erreicht, bleibt mein Spiel hängen, weil er denselben Ausführungsstrang blockiert, der Alice zum Laufen bringen würde.
Ich überlegte, jede Funktion dazu zu bringen, eine Aktion alice.walk_to(bob)
in die Warteschlange zu stellen - ein Objekt in eine Warteschlange zu schieben, das auftauchte, sobald Alice Bob erreichte, wo immer er sich befand. Das ist subtiler gebrochen: dieif
Zweig wird sofort ausgewertet, sodass Bob Alice vielleicht begrüßt, selbst wenn er ihr den Rücken zugewandt ist.
Wie gehen andere Engines / Leute mit Skripten um, ohne Threads zu erstellen? Ich fange an, in Bereichen, die keine Spieleentwickler sind, wie jQuery-Animationsketten, nach Ideen zu suchen. Es scheint, dass es einige gute Muster für diese Art von Problem geben sollte.
Antworten:
So etwas wie Panda macht man mit Rückrufen. Anstatt zu blockieren, wäre es so etwas wie
Mit einem vollständigen Rückruf können Sie diese Art von Ereignissen so tief wie Sie möchten verketten.
BEARBEITEN: JavaScript-Beispiel, da dies eine bessere Syntax für diesen Stil hat:
quelle
Der Begriff, nach dem Sie hier suchen möchten, ist " Koroutinen " (und normalerweise ist dies das Sprachschlüsselwort oder der Funktionsname
yield
).Die Implementierung hängt in erster Linie von Ihrer Sprache ab. Für ein Spiel möchten Sie, dass die Implementierung so leicht wie möglich ist (leichter als Fäden oder sogar Fasern). Die Wikipedia-Seite (verlinkt) enthält einige Links zu verschiedenen sprachspezifischen Implementierungen.
Ich habe gehört, Lua hat eine eingebaute Unterstützung für Coroutinen. Genauso wie GameMonkey.
UnrealScript implementiert dies mit den so genannten "Zuständen" und "latenten Funktionen".
Wenn Sie C # verwenden, können Sie sich diesen Blog-Beitrag von Nick Gravelyn ansehen.
Darüber hinaus ist die Idee der "Animationsketten" eine praktikable Lösung für dasselbe Problem, obwohl sie nicht dasselbe ist. Nick Gravelyn hat auch eine C # -Implementierung davon .
quelle
yield return walk_to();
in Ihr Skript.Sich nicht verstricken zu lassen ist klug.
Die meisten Game-Engines arbeiten als eine Reihe von modularen Phasen, wobei die einzelnen Phasen von Arbeitsspeicher gesteuert werden. Als Beispiel für das Gehen haben Sie normalerweise eine KI - Phase, in der Ihre gelaufenen Charaktere sich in einem Zustand befinden, in dem sie nicht nach zu tötenden Feinden suchen sollten, eine Animationsphase, in der Animation X ausgeführt werden sollte, eine Physikphase (oder Simulationsphase), in der ihre aktuelle Position aktualisiert wird usw.
In Ihrem obigen Beispiel ist 'alice' ein Schauspieler, der aus Teilen besteht, die in vielen dieser Phasen vorkommen. Ein blockierender actor.walk_to-Aufruf (oder eine Coroutine, die Sie einmal pro Frame als nächstes aufrufen ()), verfügt daher wahrscheinlich nicht über den richtigen Kontext viele Entscheidungen treffen.
Stattdessen würde eine 'start_walk_to'-Funktion wahrscheinlich etwa Folgendes tun:
Anschließend werden in der Hauptschleife der Tick ai, der Tick physics, der Tick animation und der Tick cutscene ausgeführt. In der Cutscene wird der Status für jedes Subsystem in Ihrer Engine aktualisiert. Das Cutscene-System sollte auf jeden Fall verfolgen, was jede seiner Cutscenes tut, und ein Coroutine-System für etwas Lineares und Deterministisches wie eine Custscene könnte Sinn machen.
Der Grund für diese Modularität liegt darin, dass die Dinge einfach und schön bleiben und dass Sie bei einigen Systemen (wie Physik und KI) gleichzeitig den Status von allem wissen müssen, um die Dinge richtig aufzulösen und das Spiel in einem konsistenten Zustand zu halten .
hoffe das hilft!
quelle