[ T. A. G. ] [ Tutorium ]

 

Kapitel 8

Klassendenken

 

8.1    Klassen von Objekten

In vielen Adventures kommen Objekte vor, die einander ähnlich sind, oder die zumindest ähnliche Eigenschafen haben. Beispiele sind hier die Schätze aus Adventure und Zork, Schriftrollen mit Zaubersprüchen, ID-Clips wie in Starrider und, und, und.

Solche Objekte lassen sich in so genannten Klassen zusammenfassen. Auf diese Weise muss man die allgemeinen Eigenschaften nur einmal definieren. Trotzdem kann jedes Objekt dieser Klasse individuelle Eigenschaften besitzen.

   Der Begriff der Klasse stammt aus der Objektorientierten Programmierung (OOP), die durch die Klassen sehr mächtig ist. Die Klassen von T.A.G. haben die wichtigsten Eigenschaften der OOP-Klassen, sind aber mehr auf reine Text-Adventure-Objekte zugeschnitten und daher nicht ganz so potent.

Eine Klasse wird genau wie ein Objekt definiert, nur beginnt der Block nicht mit Obj, sondern mit dem Schlüsselwort ObjKlasse. Für unser kleines Adventure wollen wir zum Beispiel die ganze Wegzehrung, die wir definiert haben, in einer Klasse zusammenfassen. Dazu definieren wir:

    ObjKlasse Verpflegung
    Name    'Verpflegung' f
    Vor     'lebens' 'nahrungs'
    Subst   'verpflegung' f  'essen' n  'mittel' p
    Subst   'mittel' n
    Ort     in Rucksack
    Besch   'Hmm, sehr nahrhaft.'
    Attr    essbar

Wenn wir das neue Adventure generieren und laufen lassen, gibt es nichts Neues. Es gibt kein Objekt Verpflegung im Rucksack. Eine Objektklasse beschreibt kein Objekt im Spiel, sondern ist lediglich eine Vorlage für Objekte.

Damit die Klasse wirksam wird, müssen wir also ein Objekt definieren, das dieser Klasse angehört. Dazu geben wir bei einer Objektdefinition die Klasse in Klammern an:

    Obj     Butterbrot (Verpflegung)

Diese Zeile reicht bereits, da alle Eigenschaften von der Klasse übernommen werden: das Butterbrot heißt »Verpflegung« und kann mit dem bei der Klasse angegebenen Vokabular angesprochen werden. Es befindet sich im Rucksack und ist essbar.

Der Nachteil ist im Moment jedoch, dass dieses Objekt keine individuellen Eigenschaften besitzt. Wenn wir mit dieser Methode noch einen Schokoriegel, eine Banane usw. definieren, sehen alle diese Dinge gleich aus, haben dasselbe Vokabular und treten nur als »Verpflegung« in Erscheinung. Das ändert sich im nächsten Abschnitt.

 

8.2    Vererbung

Wie wir eben gesehen haben, übernimmt ein Objekt alle Eigenschaften der Klasse. Man spricht hier von Vererbung. Trotzdem können zu jedem Objekt, das einer Klasse angehört (man sagt dazu auch Instanz der Klasse), individuelle Eigenschaften gegeben werden. Je nach Eigenschaft geschieht die Verebrung aber auf verschiedenem Wege:

Ersetzende Verebung
Bei Eigenschaften, die jedes Objekt logischerweise nur einmal haben kann, werden die geerbten Eigenschaften durch die individuellen ersetzt. Das ist der Fall beim Namen, bei den Beschreibungstexten (Besch, Erst, Darauf usw.), bei Zuständen, Ortsangaben und allgemeinen Variablen wie Gew, Vol usw.

Erweiternde Vererbung
Andere Eigenschaften, wie z.B. das Vokabular und die Ausführungsblöcke werden dem Objekt einfach hinzugefügt. das Objekt besitzt also das Vokabular der Klasse und des Objekts.

Das klingt vielleicht etwas verwirrend, deshalb hier ein paar Beispiele für unsere Verpflegungsklasse:

    Obj     Butterbrot (Verpflegung)
    Name    'Butterbrot' n
    Vor     'butter'
    Subst   'brot' n  'schnitte' f
    Ort     in Dose

Damit ist unser Objekt Butterbrot schon wesentlich individueller, es kann als Brot angesprochen werden und heißt auch nicht mehr »Verpflegung«, sondern »Butterbrot«. Es kann aber, dank der Vokabeldefinition der Klasse, als »Verpflegung« angesprochen werden.

Weiter geht's:

    Obj     Banane (Verpflegung)
    Name    'Banane' f
    Adj     'krumm' 'gelb'
    subst   'banane' f  'frucht' f
    Besch   'Die Banane ist eine krumme, gelbe Frucht,
            die hoffentlich der EU-Norm entspricht.
            (Oder besser: hoffentlich nicht.)'

Damit haben wir für die Banane die Standard-Beschreibung der Klasse ersetzt.

Ein Objekt übernimmt natürlich auch die Attribute der Klasse. Mit Attr kann man neue Attribute hinzufügen, mit einem vorangestellten Schrägstrich aber auch wieder wegnehmen:

    Obj     Karotte (Verpflegung)
    Name    'phosphoresziered^ Plastik-Möhre'
    adj     'phosphoreszierend' 'leuchtend'
    vor     'leucht' 'phosphor' 'plastik'
    subst   'karotte' f  'möhre' f
    Attr    /eßbar Lichtquelle

Etwas komplizierter ist das Vererben von Ausführungsblöcken. Um dies zu veranschaulichen, erweitern wir unsere Klassendefinition:

    ObjKlasse Verpflegung
    Name    'Verpflegung' f
    vor     'lebens' 'nahrungs'
    subst   'verpflegung' f  'essen' n  'mittel' p
    subst   'mittel' n
    Ort     in Rucksack
    Besch   'Hmm, [der selbst] sieht sehr lecker aus.'
    Attr    eßbar
    NachAusf
        (essen)
            Wenn (Hunger > 5) dann
                dekr Hunger 5
            sonst
                lösche Hunger
            Ende
    EndeAusf

Selbst ist eine Variable, die das tatsächlich angesprochene Objekt meint. (Wir erinnern uns, dass die Klasse selbst nicht als Objekt im Spiel auftaucht. In den Ausführungsblöcken der Klasse muss daher selbst benutzt werden.) In der Regel ist selbst aber mit aObj oder aObj2 identisch.

Mit der NachAusf wird der Hunger getilgt. (Dabei wird angenommen, dass Hunger eine globale Variable ist.) Alle Verpflegungen verringern den Hunger um fünf Einheiten. Wir können diese Ausführung aber erweitern, wobei der individuelle Ausführungsblock des Objekts aber vor dem entsprechenden Block der Klasse ausgeführt wird:

    Obj     Landjäger (Verpflegung)
    ...
    NachAusf
        (essen)
        Text 'Der Landjäger ist ziemlich fettig, aber
            egal - du bist jetzt weniger hungrig.'
    EndeAusf

    Obj     fauler_Apfel (Verpflegung)
    ...
    NachAusf
        (essen)
        Stop 'Bah! Der faule Apfel bekommt dir gar
            nicht und du spuckst die Überreste sofort
            aus.'
    EndeAusf

Bei diesen beiden Objekten wird der Text des Essens nachträglich abgeändert. Beim Landjäger wird nachher allerdings die Reduzierung des Hungers durchgeführt, beim Apfel nicht, da hier die Handlung mit Stop abgebrochen wurde.

 

8.3    Variablen für Objekte

Zu Objekten können Variablen definiert werden, die allerdings nur Zahlenwerte von 0 bis 255 annehmen können. Dabei muss der Variablenbezeichner von allen anderen IDs verschieden sein. Verschiedene Objekte können aber dieselbe Variable besitzen. Diese Variablen werden im Objektblock mit

    Var [Variablen-ID] [Wert]

definiert, der Wert kann wie immer weggelassen werden, er ist dann Null. In Ausführungsblöcken spricht man die Variablen mit

    [Objekt].[Variablen-ID]

an. Dabei kann Objekt ein Objekt oder eine Objektvariable sein, und das macht die Variablen für Objekte natürlich besonders nützlich. Ein Objekt, bei dem eine Variable nicht definiert wurde, besitzt sie einfach nicht. Man kann sie dann nicht ändern, und wenn man sie lesen will, ist sie immer Null. Deshalb muss man vorher schon einmal nachfragen, ob ein Objekt eine Variable überhaupt besitzt. Das geht mit folgender Bedingung:

    ([Objekt] besitzt [Variablen-ID])

Diese Variablen sind natürlich nicht an eine Klassendefinition gebunden. Aber man kann sie im Zusammenhang mit Klassen gut einsetzen. Ist bei einer Klasse eine Variable definiert, so haben alle Objekte dieser Klasse auch diese Variable, und zwar mit dem bei der Klasse definierten Wert. Der Wert kann natürlich in der individuellen Objektdefinition überschrieben werden.

So können wir für unsere Verpflegungsklasse den Sättigungsgrad angeben, denn ein Baguette mit Schinken, Käse und Ei sättigt doch mehr, als eine Tomate.

    ObjKlasse Verpflegung
    ...
    Var Nährwert 5
    NachAusf
        (essen)
            Wenn (Hunger > selbst.Nährwert) dann
                dekr Hunger selbst.Nährwert
            sonst
                lösche Hunger
            Ende
    EndeAusf

Damit ist genau dasselbe erreicht wie vorher: Alle Verpflegungs-Objekte machen den Spieler um 5 Nahrungseinheiten satter. Aber wir können nun diese Sättigung leicht für verschiedene Objekte anpassen:

    Obj     Landjäger (Verpflegung)
    Var     Nährwert 7
    ...

    Obj     Banane (Verpflegung)
    ...

    Obj     BigMac (Verpflegung)
    Var     Nährwert 1
    ...

Und so weiter. Die Banane als Durchschnittsverpflegung behält hier den Nährwert von fünf. (Ernährungsfanatiker würden natürlich Variablen für Eiweiß, Kohlehydrate und Fett definieren, aber als erste Näherung kann man sich wohl mit der Angabe eines allgemeinen Nährwerts zufrieden geben.)

Variablen für Objekte sind also sinnvoll, wenn mehrere Objekte - typischerweise natürlich welche, die zu einer Klasse zusammengefaßt sind - dieselbe Eigenschaft besitzen. Eine Variable nur für ein Objekt zu definieren, macht wenig Sinn, dann sollte man lieber eine globale Flagge benutzen.

Aufgabe 22 **
Definiere eine Klasse von Zaubertränken, die verschiedene Farben und eine der drei Wirkungen Heilung, Gift oder Sättigung haben.

Es gibt übrigens Variablen für Objekte, die wir bereits kennen, und die alle Objekte besitzen: [Objekt].Gew und [Objekt].Vol. Zu jedem Objekt können bis zu acht zusätzliche Variablen angegeben werden. Räume können in T.A.G. keine Variablen besitzen.

 

8.4    Ordnung halten mit Klassen

Objektklassen bieten einen weiteren, schönen Effekt: Man kann in Aufzählungen, wie sie z.B. in der Liste der Dinge in einem Raum oder einem Behälter auftauchen, Objekte einer Klasse zusammenfassen.

Dazu gibt es den Plural, der zusätzlich zum Namen definiert werden kann. (Da unsere Verpflegung individuelle Namen besitzt, muss der Name der Klasse nicht angegeben werden.) Wir ändern die Definition der Verpflegung auf

    ObjKlasse Verpflegung
    Plural  'Verpflegung' f 3
    ...

und schon sieht die Beschreibung des Rucksacks folgendermaßen aus:

Im Moment sind etwas Verpflegung (ein Landjäger, ein Butterbrot und ein nicht mehr ganz frischer Apfel), eine Landkarte und ein Anorak im Rucksack.

Wenn die Liste lang ist, finde ich diese Darstellung übersichtlicher. Die 3 nach dem f für feminin bewirkt übrigens, dass es nicht »eine Verpflegung«, sondern »etwas Verpflegung« heißt. (Außerdem sieht man hier, dass der Plural nicht unbedingt im Plural stehen muss.)

Es geht aber noch weiter. Stellen wir uns einmal eine Reihe von Bällen vor:

    ObjKlasse Ball
    Plural  'Bälle' p
    subst   'ball' m  'bälle' p
    ...

    Obj     blauer_Ball (Ball)
    Name    'blau^ Ball' m
    Adj     'blau'
    ...

Mit dieser und weiteren, ähnlichen Definitionen, sähe eine Liste folgendermaßen aus:

In dem Korb siehst Du vier Bälle (einen blauen Ball, einen roten Ball, einen bunten Ball und einen schwarz-weißen Ball).

Diese Aufzählung hätte unser Deutschlehrer wohl mit einer roten Schlange und einem »A« für Ausdrucksfehler bedacht. Um solch unschöne Wortwiederholungen bei ähnlichen Objekten zu vermeiden, kann man den Objekten der Klasse einen Listennamen (lName) geben:

     Obj     blauer_Ball (Ball)
     Name    'blau^ Ball' m
     lName   'blau^' m
     Adj     'blau'
     ...

Nun sieht die Aufzählung so aus:

In dem Korb siehst Du vier Bälle (einen blauen, einen roten, einen bunten und einen schwarz-weißen).

Wenn beim Plural nach der Geschlechtsangabe nichts angegeben wird (oder eine 0), so werden die Objekte, die gruppiert werden, durchgezählt und das Zahlwort vorangestellt.

Wenn der Spieler nun »nimm den Ball« eingibt, so erscheint die standardmäßige Nachfrage: "Welchen Ball? Den blauen, den roten, den bunten oder den schwarz-weißen?" Bei dieser Nachfrage werden auch die Listennamen, wenn vorhanden, benutzt.

Ist die Eingabe aber »Nimm einen Ball«, so sucht das Programm eigenmächtig einen aus, da wegen des unbestimmten Artikels angenommen wird, dass es dem Spieler egal ist, welchen. (Es wird wohl der zuerst erwähnte sein, also der blaue.) Man kann sogar »Nimm zwei Bälle« oder »Hebe alle Bälle außer den bunten auf« angeben. Das funktioniert natürlich nur bei Befehlen, die Mehrfachobjekte zulassen, wie nehmen und hinlegen. Die Eingabe mit unbestimmtem Artikel oder Zahlwort wird bei allen Objekten erkannt, die gemeinsames Vokabular haben, aber bei Klassen ist die Definition natürlich wesentlich einfacher.

Zum guten Schluss lassen sich mit Klassen gleiche Objekte zusammenfassen. Gleiche Objekte sind Objekte, die derselben Klasse angehören, und die kein individuelles Vokabular besitzen. Diese Objekte sind also nie einzeln ansprechbar, sondern immer nur als Objekt einer Klasse. Die Gruppierung funktioniert wie oben, nur, dass die Objekte nicht zusätzlich in Klammern aufgelistet werden:

    ObjKlasse Silbermünze
    Plural  'Silbermünzen'
    ...

    Obj     S1 (Silbermünze)
    Obj     S2 (Silbermünze)
    Obj     S3 (Silbermünze)
    Obj     S4 (Silbermünze)

    ObjKlasse Kupfermünze
    Plural  'Kupfermünzen'
    ...

    Obj     K1 (Kupfermünze)
    Obj     K2 (Kupfermünze)

Die Liste lautet hier z.B. »Hier sind vier Silbermünzen und zwei Kupfermünzen«. Obwohl die Münzen gleich sind, müssen sie unterschiedliche IDs besitzen. Für das Programm sind sie also doch verschieden.

Aufgabe 23 *
Erzeuge ein Gefäß mit einem Rostlösemittel. Erzeuge weiterhin einen Satz von verrosteten Münzen, die zunächst nicht unterscheidbar sind, sich aber nachdem sie in den Rostlöser getaucht wurden, in verschiedene Münzen verwandeln: eine Dublone, eine Pesete usw.

 

8.5    Raumklassen

So wie es Klassen für Objekte gibt, so gibt es auch Raumklassen. Hier werden der Name und die Ausgänge ersetzend vererbt, die Ausführungsblücke erweiternd. In der Praxis sind Raumklassen eigentlich nur interessant, wenn sie Ausführungsblöcke besitzen. Das Schlüsselwort für eine Raumklasse ist - wer weiß es? - RaumKlasse.

Als kurzes Beispiel soll hier eine Raumklasse für ein Labyrinth definiert werden, in der verhindert wird, dass der Spieler Gegenstände auf den Boden legt, um eine Karte zu zeichnen:

    RaumKlasse Irrgarten
    Name    'Irrgarten'
    Std     Wand
    Attr    Laby
    Besch   'Der Irrgarten ist dunstig und neblig,
            dicke Schwaden ziehen über den Boden.'
    NachAusf
        (legen)
            Text 'Bald wabern die Nebelschwaden über
                [den aObj], und [er] verschwinde[t]
                - für immer.'
    NachAusf

Nun muss der Autor nur noch einzelne Räume erzeugen, die lediglich Informationen über die Ausgänge enthalten müssen. Die Definition von Raumklassen funktioniert analog zu den Objektklassen, wird aber weitaus weniger benutzt. Deshalb soll dieser kurze Absatz zu Raumklassen genügen.

Zusammenfassung:
Um mehrere ähnliche Objekte zu implementieren, können Objektklassen definiert werden, die nicht im Spiel als Objekt auftauchen, aber als Vorbilder für tatsächliche Objekte dienen.

Die Eigenschaften der Klasse werden auf Objekte dieser Klasse übertragen, man spricht hier von Vererbung.

Ausführungsblöcke und Vokabular werden beim Vererben ergänzt, alle anderen Eiganschaften ersetzt.

Zu jedem Objekt können bis zu acht Variablen definiert werden. Durch eine Definition derselben Variablen bei anderen Objekten kann man so flexibel auf diese Werte zugreifen.

In T.A.G. können Klassen auch dazu benutzt werden, um Objekte bei Listen zu gruppieren oder um eine Anzahl von identischen Objekten zu beschreiben.

Es können auch Klassen für Räume erzeugt werden.

Verweise:
Handbuch, Kapitel 3.3: Klassen von Objekten und Räumen
Handbuch, Kapitel 11: Aufzählungen und Beschreibungen
Objektklasse Edelstein in karn.adv


Nächstes Kapitel
Voriges Kapitel
nach oben
Inhalt


________
Startseite --> Textadventures --> T.A.G. --> Tutorium --> Kapitel 8
Übersicht
Martin Oehm

[ www.martin-oehm.de - Startseite ]
[ Textadventures ]
[ T. A. G. ]

Inhalt

Einleitung
Spielwelt
Objekte I
Objekte II
Anweisungen
Variablen
Befehle
Klassen
Personen
Diverses

Lösungen