Über den prinzipiellen Aufbau und die Arbeitsweise der Engine möchte ich Euch heute bisschen was erzählen. Dazu schauen wir uns mal das folgende Diagramm an:
In der Scriptdatei steht, was dann in der Szene passieren soll:
[LOOK CLOCK]
STONE: GotoXY(700, 550); // Stone geht zur Uhr.
WaitForActor(); // warten, bis Stone die Uhr erreicht hat
STONE: Say("Es ist kurz vor 12."); // Stone liest die Uhr ab.
WaitForActor(); // warten, bis Monolog beendet ist
STONE: TurnToDirection(Down); // Stone dreht sich zum Spieler
STONE: Say("Juhu, fast Mittagspause."); // ... und freut sich über die bevorstehende Pause
[END]
Zurück zum Diagramm:
Diese Scriptdatei wird von der Engine in den Speicher geladen. Gibt der Spieler also den Befehl, dass sich Stone die Uhr anschauen soll, dann sucht die Engine in der Scriptdatei nach dem Scriptblock "LOOK CLOCK". Hat sie ihn gefunden, dann wird die erste Befehlszeile ausgelesen.
Diese Befehlszeile wird zunächst dem Parser zur Analyse (also zur Identifizierung des Befehls und zum Erkennen der übergebenen Parameter) zugesendet. Dieser überprüft auch, ob der Befehl von einem Spielecharakter ausgeführt werden soll oder ob es ein Befehl ist, der Hintergrundprozesse steuert (wie z.B. das Abspielen eines Sounds). Nachdem oben stehenden Beispiel-Script erhält der Parser also zu Beginn die folgende Scriptzeile:
STONE: GotoXY(700, 550);
Der Parser deutet den Text vor dem Doppelpunkt als eine ID, die für einen Spielecharakter (Actor) steht, der den nachfolgenden Befehl ausführen soll (in unserem Fall Charles Stone). Diese ID speichert der Parser ab.Der Text zwischen dem Doppelpunkt und der sich öffnenden Klammer wird vom Parser als Befehl für den Actor interpretiert. Auch dieser wird abgespeichert. Innerhalb der Klammern stehen die Zielkoordinaten als Parameter, die der Parser ebenfalls einzeln im Speicher ablegt. Damit ist die Analyse des Befehls abgeschlossen und der Parser liefert der Engine die folgenden Daten zurück:
currentActor: STONE
currentCommand: GotoXY
parameter1: 700
parameter2: 550
Die Engine nimmt diese aufbereiteten Daten entgegen und weiß jetzt, dass sie den Actor STONE anweisen muss, sich zu den Koordinaten (700, 550) zu bewegen. Da jeder Actor ein eigenständiges Objekt ist, führt dieser den Befehl umgehend und selbständig aus.
Nachdem die Engine dem Actor den Befehl gegeben hat, holt sie sich die nächste Scriptzeile. In ihr steht:
WaitForActor();
Auch hier übergibt sie die Zeile dem Parser, der erkennt, dass diesmal ein Befehl kommt, der von keinem Actor auszuführen ist, sondern einen Hintergrundprozess startet (die Zeile beginnt schließlich nicht mit einer Actor-ID). Parameter sind zwischen den runden Klammern auch nicht zu finden und deshalb liefert der Parser der Engine nur zurück:
currentActor: -
currentCommand: WaitForActor
Die Engine nimmt diese Daten wieder entgegen und startet einen Hintergrundprozess, der die weitere Ausführung des Scriptblockes so lange stoppt, bis unser Actor STONE bei den Koordinaten (700, 550) angekommen ist. Erst, wenn der Actor der Engine zurückmeldet "Hey, ich bin am Ziel angekommen!" wird die nächste Scriptzeile dem Parser übergeben:
STONE: Say("Es ist kurz vor 12.");
currentActor: STONE
currentCommand: Say
parameter1: "Es ist kurz vor 12."
Die Engine gibt daraufhin dem Actor STONE den Befehl etwas zu sagen, nämlich "Es ist kurz vor 12."
Der nächste WaitForActor-Befehl lässt die Engine warten, bis Stone seinen Text fertig gesprochen hat. Die übrigen Scriptzeilen laufen von der Analyse her analog ab.
Das war's vom Prinzip her schon. Man könnte also sagen, die Engine ist ein Manager, der nicht lesen kann und deshalb Hilfe von einem Assistenten (Parser) braucht, damit er trotzdem seine Angestellten durch die Gegend scheuchen kann:
Engine: "Ich hab hier etwas stehen, das ich nicht lesen kann. Parser, sag mir, was da steht!"
Parser: "Hier steht, dass Hermann Kaffee holen soll."
Engine: "Hey Hermann, hol Kaffee! Aber zackzack!"
Hermann: "Wird gemacht, Chef!"
Grüße, Roland