Blog Info Screens Videos Impressum/Datenschutz

Arbeitsweise der "Rodelberg-Engine"

... und weiter geht's mit einem Einblick in unsere Arbeit. Nachdem unser kleiner Bewegungs-Test geklappt hat (siehe "Vor ziemlich genau 2 Jahren"), haben wir unsere Arbeit aufgeteilt. Robin hat sich mit der Theorie sowie der Programmierung des Pathfinding-Algorithmus beschäftigt. Außerdem hat er sich um die Darstellung und Programmierung der Animationsphasen aller Personen und Gegenstände gekümmert, die im Spiel zu sehen sein werden. Mein Part war das Entwerfen der Scriptsprache und die Programmierung der Engine, die schließlich die Scriptbefehle ausführen musste.

Ü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:


Die Script-Datei ist quasi das "Drehbuch" einer Szene, die sich gerade auf dem Bildschirm abspielt. Sie beinhaltet Regieanweisungen, die auszuführen sind, wenn ein bestimmtes Ereignis eintritt oder wenn der Spieler unserem Protagonisten einen Befehl gibt. In dem folgenden Beispiel bekommt Charles Stone vom Spieler den Befehl, sich die Uhr an der Wand anzuschauen.


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]


Andere Scriptblöcke, wie [TAKE CLOCK], [USE CLOCK], [LOOK PLANT], usw. sind analog dazu ebenfalls in der Scriptdatei zu finden.

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.");

Na, was passiert jetzt wohl? ;-) Genau: der Parser analysiert die Zeile und liefert der Engine zurück:

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

Blog-Archiv

Labels