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 |