Type
Type ist eine sehr wichtige Erweiterung des BlitzBasic-Dialekts. Es hat große Ähnlichkeiten mit STRUCT in C++. Besonders Anfänger haben bei Verwendung von Types große Schwierigkeiten. Darum sollen Types überlegt eingesetzt werden. Oft ist es aber trotzdem unvermeidbar.

Types sind eine Ansammlung von Eigenschaften. Bestes Beispiel dafür sind "Space Invaders". Alle Aliens benötigen bestimmte Eigenschaften - z.B. Position auf dem Bildschirm. Nun kann man ja jedem Alien einige Variablen zuweisen: alien1x, alien1y, alien2x, alien2y... Dies ist aber sehr aufwendig. Man könnte auch mehrere DIM-Felder erzeugen und alle Daten dort abspeichern. Leider ändert sich ja die Anzahl der Aliens. Speziell für dieses Problem wurden Types entwickelt.


Definieren
TYPE definiert eine Kollektion. Mit FIELD können beliebig viele Eigenschaften zu der Kollektion hinzugefügt werden. Es können auch unterschiedliche Variablentypen mit %, # oder $ hinter dem Eigenschaftsnamen definiert werden:

TYPE {typename}
   FIELD {eigenschaft}
   FIELD {eigenschaft}
   ...
END TYPE


So, nun braucht man eine Container-Variable. Diese Variable sollte möglichst mit LOCAL (nur vom Hauptprogramm oder Unterprogramm lesbar) oder mit GLOBAL (überall lesbar) definiert werden:

LOCAL {variable}.{typename}

Normalerweise sollte man immer {variable}.{typename} zusammen angeben. Da es nun definiert wurde und BlitzBasic weiß, dass die Variable zum Type zugehört, so kann man {typename} weglassen und nur {variable} angeben. Dies werde ich auch in nachfolgenden Beispielen so machen.


Hinzufügen
Nun wird es schwieriger: Wir müssen ein Objekt zur Kollektion hinzufügen. Dazu gibt es den Befehl NEW:

{variable}=NEW {typename}

Das war's auch schon. Egal wie die Variable heißt, dieses Objekt wird zu ein und der selben Type-Kollektion hinzufügt. Das einzige was man noch tun muss, ist eine Wertzuweisung:

{variable}\{eigenschaft}=Wert

Mit NEW werden danach weitere Objekte hinzugefügt. Dazu kann ein und die selbe Container-Variable benutzt werden, da ja alles zu der selben Type-Kollektion hinzugefügt wird. Beachte auch: Nur mit NEW lassen sich weitere Objekte hinzufügen. Der nachfolgende Code demonstriert das:

TYPE alien
   FIELD wert
END TYPE
a.alien=NEW alien
b.alien=a
a\wert=1
PRINT b\wert


Bei diesem Beispiel wurde ein Objekt nicht kopiert, sondern die Container-Variable "b" zeigt jetzt auf das selbe Objekt wie "a". Ändert man einen Wert in "a", so ändert es sich auch in "b".


Abfragen
Im Programm muss man diese Werte dann auslesen können. Dazu muss man nur folgendes tun:

Wert={variable}\{eigenschaft}

Wie man sieht, funktioniert es so ähnlich wie bei normalen Variablen. Auch Berechnungen sind möglich:

TYPE alien
   FIELD x
   FIELD y
END TYPE
a.alien=NEW alien
a\x=10
a\y=20
PRINT a\x*a\y


Meistens werden aber alle Objekte nacheinander abgefragt - und zwar mit der FOR...NEXT Schleife:

FOR {variable}=EACH {typename}
   Wert={variable}\{eigenschaft}
NEXT


Auswählen
Nun möchte man manchmal nicht alles einfach vom Anfang bis Ende abarbeiten. Jeder Eintrag lässt sich problemlos manuell auswählen. Dazu gibt es folgende Befehle:

Mit AFTER wird der nächste Eintrag ausgewählt:

{variable}=AFTER {variable}

Mit BEFORE wird vorheriger Eintrag ausgewählt:

{variable}=BEFORE {variable}

Mit FIRST wird der erste Eintrag ausgewählt:

{variable}=FIRST {typename}

Mit LAST wird der letzte Eintrag ausgewählt:

{variable}=LAST {typename}

Aber Vorsicht: Will man zu einem Eintrag wechseln, der gar nicht existiert, dann kommt eine Fehlermeldung. Mit NULL kann man prüfen, ob es überhaupt vorhanden ist:

IF {variable}=NULL THEN {existiert nicht}

Somit lässt sich die FOR...NEXT Schleife auch durch das ersetzten:

{variable}=FIRST {typename}
WHILE {variable}<>NULL
   Wert={variable}\{eigenschaft}
   {variable}=AFTER {variable}
WEND




Löschen
Nun ist es oft erforderlich ein erstelltes Objekt wieder zu löschen. Dazu wurde der Befehl DELETE entwickelt:

DELETE {variable}

Allerdings wird somit nur ein Objekt gelöscht. Wenn man komplette Type-Kollektion löschen will, dann muss man es so machen:

DELETE EACH {typename}


Verschieben
Ein weiterer Vorteil von Types: Die Objekte lassen sich verschieben. Ein Objekt, welches am Anfang stand, kann somit zum Ende verschoben werden. Hierfür wurde der Befehl INSERT entwickelt:

INSERT {variable} {position} {typename}

Die {position} ist eine Kombination der Befehle AFTER, BEFORE, FIRST und LAST. Hier einige Beispiele:

Verschiebt ein Objekt nach vorne:

INSERT {variable} BEFORE FIRST {typename}

Verschiebt ein Objekt nach hinten:

INSERT {variable} AFTER LAST {typename}

Verschiebt ein Objekt um einen Eintrag nach vorne:

INSERT {variable} BEFORE BEFORE {typename}

Verschiebt ein Objekt um einen Eintrag nach hinten:

INSERT {variable} AFTER AFTER {typename}

Außerdem gibt es noch einige Tricks wie diese:

a=NEW mytype
b=NEW mytype
INSERT b BEFORE a


Leider kann man Objekte nicht an eine beliebige Stelle verschieben. Besonders bei großen Kollektionen kann es schwierig werden. Dies wird besonders bei Sortierung von Types deutlich.



Kombination Type+Dim
Types lassen sich mit DIM-Feldern kombinieren. Dadurch bleibt jedes Objekt direkt ansprechbar, dafür wird so ein Type aber nicht mehr dynamisch. Dazu muss man ein DIM-Feld so definieren:

DIM {feldname}.{typename} (index)

Mit NEW werden neue Objekte erstellt:

{feldname} (index)=NEW {typename}

Eigentlich ändert sich danach nur wenig, nur der Index wird ständig angegeben. Hier ein größeres Beispiel:

TYPE tile
   FIELD image
   FIELD extra
END TYPE

DIM tile.tile(99,99)
tile(0,0)=NEW tile
tile(0,0)\image=0
tile(0,0)\extra=0


Kombination Type+Blitzarray
Types lassen sich mit Blitzarrays kombinieren. Dadurch bleibt jedes Objekt direkt ansprechbar, dafür wird so ein Type aber nicht mehr dynamisch. Das ganze funktioniert so ähnlich wie bei DIM/TYPE-Kombination:

TYPE obj
   FIELD x
   FIELD y
END TYPE

LOCAL obj.obj[100]
obj[0]=NEW obj
obj[0]\x=0
obj[0]\y=0


Das ist aber noch nicht alles. Weiterhin lassen sich Blitzarrays als Eigenschaft mit dem Befehl FIELD definieren. Dies kann völlig neue Möglichkeiten eröffnen:

TYPE obj
   FIELD x
   FIELD y
   FIELD image[10]
END TYPE

DIM feld.obj(100)
feld(0)=NEW obj
feld(0)\x=0
feld(0)\y=0
feld(0)\image[10]=0


Angehängte Types
Manchmal gibt es Situationen, wo einige Objekte erweitere Eigenschaften haben müssen. Stell Dir vor, Du schreibst ein Spiel. In dem Spiel müssen Fabrikdaten gespeichert werden. Nun gibt es ja viele unterschiedliche Fabriken. Man muss Preis, Fläche und Stromkosten bei jeder Fabrik speichern können. Bei einer Papierfabrik muss man noch Bäumeverbrauch und Papiermenge zusätzlich speichern können. Dafür kann man angehängte Types benutzen:

TYPE fabrik
   FIELD preis
   FIELD flaeche
   FIELD stromkosten
   FIELD papier.papier
END TYPE

TYPE papier
   FIELD baemeverbrauch
   FIELD papiermenge
END TYPE

LOCAL fabrik.fabrik

fabrik=NEW fabrik
fabrik\preis=1000000
fabrik\flaeche=1000
fabrik\stromkosten=9999
fabrik\papier=NEW papier
fabrik\papier\baemeverbrauch=1111
fabrik\papier\papiermenge=123


Die Fabrik "Papier" ist ein angehängtes Type und wird als Eigenschaft an Type "Fabrik" angehängt. Dies eröffnet völlig neue Dimensionen.


Types und Handles
Jedes Objekt kann eine Handle-Nummer zurückliefern. Über diese Nummer bleibt es immer ansprechbar. Es wird immer ein Integerwert zurückgeliefert. Diese Nummer kann immer nur ein einziges Objekt haben. Solange HANDLE auf das Objekt nicht angewendet wurde, ändert sich die Handle-Nummer ständig. Erst beim ersten HANDLE-Aufruf wird eine feste ID-Nummer vergeben und zurückgeliefert.

id=HANDLE( {variable} )

Um wieder ein Objekt aus dieser ID-Nummer zu erstellen, muss man den Befehl OBJECT verwenden. Die ID-Nummer muss logischerweise ein Integerwert sein:

{variable}=OBJECT.{typename}(id)

Ob es nun sinnvoll ist oder nicht, es gibt manchmal Situationen, wo man ohne diese Befehle nur große Schwierigkeiten bei der Programmierung hätte.

TYPE window
   FIELD x
   FIELD y
   FIELD w
   FIELD h
END TYPE

win=createwindow(x,y,w,h)
drawwindow(win)

FUNCTION createwindow(x,y,w,h)
   obj.window=NEW window
   obj\x=x
   obj\y=y
   obj\w=w
   obj\h=h
   RETURN HANDLE(obj)
END FUNCTION

FUNCTION drawwindow(id)
   obj.window=OBJECT.window(id)
   ;...
END FUNCTION


Types und Functionen
Man kann auch über Funktionen auf Type-Kollektion zugreifen. Type-Kollektion ist an sich immer global verfügbar - man braucht lediglich eine Container-Variable. Wurde eine Container-Variable als global definiert, dann ist sowieso der Zugriff vom ganzen Programm möglich. Wenn lokale Container-Variable verwendet wird, dann kann man die trotzdem genau so einfach wie normale Variablen übergeben. Beachte auch, dass Änderungen IMMER wirksam werden! Hier ein kleines Beispiel:

TYPE test
   FIELD wert
END TYPE

test.test=NEW test
test\wert=1
verdoppeln(test)
PRINT test\wert

FUNCTION verdoppeln(name.test)
   name\wert=name\wert*2
END FUNCTION


Das ist auch möglich:

TYPE test
   FIELD wert
END TYPE

test.test=NEW test
test\wert=1
ausgeben()

FUNCTION ausgeben()
   FOR name.test=EACH test
      PRINT name\wert
   NEXT
END FUNCTION