Blog Info Screens Videos Impressum/Datenschutz

Der Aufbau einer Spielszene – Von Klassen, Objekten und Schauspielern

Objektorientierte Programmierung (OOP) ist schon eine tolle Erfindung. Unvorstellbar, dass wir damals zu unseren Programmieranfängen auf dem C64 vieles in Basic über Gosubs - und noch schlimmer über Gotos - programmieren mussten. Frameworks? Fehlanzeige. Spielecharaktere, die sich selbst verwalten konnten? Nope. Der Vorteil der OOP ist, dass man sich einen Bauplan ("Klasse" genannt) für einen Spielecharakter definieren kann, auf dessen Basis beliebig viele unterschiedliche Spieler ("Objekte" / "Instanzen der Klasse") "gebaut" werden können.


In der Grafik ist zu sehen, dass es einen Bauplan (Klasse) für Monster gibt. Diese Klasse beinhaltet die Beschreibung, welche Eigenschaften und Fähigkeiten ein allgemeiner Gegner haben soll. Auf dieser Klasse aufbauend können nun verschiedene Gegnerarten erstellt werden (Orks, Drachen, Zombies, …). Alle diese "Instanzen der Klasse CGegner" sind Charaktere im Spiel, die dem Spieler gefährlich werden können.

Aber was hat das jetzt mit dem Aufbau einer Szene für unser Spiel zu tun? Ganz einfach. Im Wesentlichen besteht jede Szene ebenfalls aus vielen Spielecharakteren. Diese werden zur Darstellung auf dem Bildschirm wie Folien zwischen einen Hintergrund und einen Vordergrund gelegt:


In der oben abgebildeten Grafik ist zu sehen, dass ganz vorn der Vordergrund dargestellt wird. Alles, womit unser Hauptcharakter Charles Stone interagieren kann, befindet sich dahinter: ein Poster, ein Farbtopf, ein Sparschwein und eine Brechstange. All das befindet sich vor einem Hintergrund, der den Rest des Raumes darstellt und vor dem sich Stone bewegen kann.

Aber nicht jeder Gegenstand ist so "lebendig" wie Stone oder sein Boss Hank, die sprechen und sich bewegen können. Es gibt auch statische Gegenstände, die in der Szene nur präsent sind und mit denen man vielleicht noch interagieren kann (anschauen, benutzen, nehmen).
Um in der Programmierung und beim Scripten nicht zwischen statischen und dynamischen Gegenständen/Schauspieler unterscheiden zu müssen, kommt nun die Idee der OOP ins Spiel.
Ich habe nämlich festgelegt: Alles, was auf dem Bildschirm zwischen Vordergrund und Hintergrund zu sehen ist, ist ein Schauspieler.
Das bedeutet: nicht nur Charles Stone, sondern auch das Sparschwein, die Brechstange und der Farbtopf sind Schauspieler. Und alle Schauspieler sollen die gleichen Eigenschaften und Fähigkeiten besitzen. So ist es zum Beispiel auch möglich, das Sparschwein sprechen zu lassen, obwohl es das eigentlich gar nicht kann (frei nach einem aktuellen TV-Werbespot: "Küss mich, ich bin ein verzaubertes Handy!"). Aber dadurch, dass es ja ein Schauspieler ist, könnte es das.

Also habe ich eine Klasse (ich nannte sie OSActor) erstellt und in ihr definiert, welche Eigenschaften (Attribute) und Fähigkeiten (Methoden) ein Schauspieler auf dem Bildschirm haben soll. Darauf aufbauend kann ich nun beliebig viele Instanzen dieser Klasse erstellen und im Szenenscript jedem dieser Instanzen Befehle geben, ohne immer im Hinterkopf haben zu müssen, ob es sich bei dem jeweiligen Gegenstand um einen "richtigen" Schauspieler oder nur um einen Statisten oder um Deko handelt. Cool, hm? :-)

Unsere Actor-Klasse besitzt also u.A. folgende Attribute und Methoden:


Für die Objective-C-Freunde unter Euch sieht die Deklaration der Attribute in der Headerdatei (OSActor.h) wie folgt aus:

@property (readonly, nonatomic) NSString            *identifier;
@property (strong, nonatomic)   NSString            *name;
@property (readonly, nonatomic) Point3d             position3D;
@property (assign, nonatomic)   BOOL                interactionAllowed;
@property (assign, nonatomic)   BOOL                isExitToAnotherScene;
@property (strong, nonatomic)   UIColor             *speechBubbleTextColor;
// Statuseigenschaften:
@property (readonly, nonatomic) BOOL                isMoving;
@property (readonly, nonatomic) BOOL                isTalking;
@property (readonly, nonatomic) BOOL                isAnimating;

Die Methoden sind wie folgt definiert:

- (void)setPosition3D:(Point3d)newPosition;
- (void)gotoActor:(OSActor*)destinationActorID;
- (void)gotoPosition:(CGPoint)newPosition
- (void)turnToDirection:(NSString*)newDirection;
- (void)stopMoving;
- (void)show;
- (void)hide;
- (void)fadeIn:(NSTimeInterval)time;
- (void)fadeOut:(NSTimeInterval)time;
- (void)setCostume:(NSString*)fileName;
- (void)playAnimation:(NSString *)animationID;
- (void)stopAnimation;
- (void)say:(NSString*)text withSpeechFileName:(NSString*)speechFileName andVolume:(float)volume
- (void)stopTalking;
- (void)addItemToInventoryWithIdentifier:(NSString*)itemIdentifier name:(NSString*)itemName  iconFileName:(NSString*)iconFileName;
- (void)removeItemFromInventoryWithIdentifier:(NSString*)itemIdentifier
- (void)clearInventory;

Wie die einzelnen Methoden programmiertechnisch gelöst sind, kann ich bei Interessse in einem anderen Artikel beschreiben. Hinterlasst einfach einen Kommentar, welche Methode Euch in der Umsetzung interessiert und dann werde ich dazu noch mal einen gesonderten Artikel verfassen.

Jedenfalls ist dank der OOP jedes Objekt nun für sich als "Individuum" ansprechbar und kann eigenständig handeln. Gebe ich im Script also z.B. den Befehl

STONE: GotoActor("POSTER");

dann wird das Objekt "Stone" angesprochen und er führt die Methode gotoActor: (OSActor*)destinationActorID; aus. Die Folge ist, dass Stone auf dem Bildschirm zum Actor mit der ID "POSTER" geht. Seine Zielkoordimaten erhält er dazu vom Objekt POSTER, das ihm die Koordinaten in Form von x, y und z-Werten mitteilt und über Robins Wegfindungsroutine sucht er sich den günstigsten Weg dorthin.Bis zum nächsten Mal!Roland

Blog-Archiv

Labels