TEIL I: Syntax und Grundlagen
Kapitel 1: Das erste Programm
Wir geben in unser Space-Event folgenden Code ein:
|
GML
|
1
2
3
4
|
{
//Das Hello-World Programm
show_message("Hello, World!");
}
|
Testspiel starten, auf die Leertaste drücken und staunen:
Analysieren wir einmal unser Programm. Die geschwungenen Klammern { und } begrenzen unser Programm. Würden wir nach } noch Code einfügen, wäre das ein Fehler.
Jeder Befehl in GML muss mit einem Semikolon oder einem Zeilenumbruch abgeschlossen werden. Ich habe mir angewöhnt, nach jedem Befehl ein Semikolon zu machen.
Die zweite Zeile ist ein Kommentar. Der Text nach "//" ist bis zum Zeilenende ein Kommentar. Kommentare werden verwendet, um das Programm übersichtlicher zu machen.
show_message erzeugt eine Dialogbox. In dieser Dialogbox wird der Text, den wir danach in runden Klammern und Anführungszeichen eingeschlossen angeben, ausgegeben. Wir hätten auch schreiben können:
|
GML
|
1
2
3
|
{
show_message("Hello, " + "World!");
}
|
Hier wird "World!" an "Hello, " angehängt und das Ergebnis - "Hello, World!" ausgegeben.
Kapitel 2: Variablen
Wie in jeder Programmiersprache gibt es in GML Variablen. Eine Variable ist ein Bereich im Speicher eines Computers, in dem Informationen abgespeichert werden können. In GML kann eine Variable entweder eine reelle Zahl oder eine Zeichenkette (String) sein.
Der folgende Code-Abschnitt zeigt die Verwendung von Variablen:
|
GML
|
1
2
3
4
5
6
7
8
9
|
{
var laenge, breite, flaeche;
laenge = 34;
breite = 78;
flaeche = laenge * breite;
show_message("Die Fläche des Rechtecks beträgt " + string(flaeche) + "m²");
}
|
Die zweite Zeile definiert drei Variablen, die ab sofort unter den Namen laenge, breite und flaeche angesprochen werden können. (Diese Zeile ist nicht unbedingt notwendig, aber sie gibt an, dass die Variablen nur in diesem Stück Code vorhanden sein sollen - das spart Speicherplatz).
Die 4. und die 5. Zeile belegen die Variablen laenge und breite mit Werten. In der 6. Zeile wird laenge mit breite multipliziert und das Ergebnis in die Variable flaeche geschrieben.
Die 8. Zeile gibt das Ergebnis aus. Das sieht noch etwas seltsam aus. Hier wird die Variable flaeche in eine Zeichenkette umgewandelt (mit string(flaeche) ) und das Ergebnis an "Die Fläche des Rechtecks beträgt " angehängt. Schließlich wird hinten noch " m²" angehängt und das endgültige Ergebnis ausgegeben. Das sieht so aus:
Dieses Programm hat noch wenig Sinn. Lassen wir doch einmal den Benutzer Werte eingeben. Dafür benötigen wir folgenden Code:
|
GML
|
1
2
3
4
5
6
7
8
9
|
{
var laenge, breite, flaeche;
laenge = get_integer("Bitte Länge eingeben", 1);
breite = get_integer("Bitte Breite eingeben", 1);
flaeche = laenge * breite;
show_message("Die Fläche des Rechtecks beträgt " + string(flaeche) + "m²");
}
|
Das einzige, was sich hier verändert hat, sind die 4. und 5. Zeile. get_integer liest eine ganze Zahl in einer Dialogbox ein. Hier wird "Bitte Länge/Breite eingeben" angezeigt. 1 ist hier der voreingestellte angezeigte Wert. Die Dialogbox sieht so aus:
Der Rest des Programms ist gleich geblieben.
Kapitel 3: Funktionen
Bis jetzt haben wir show_message, get_integer und string verwendet, ohne zu wissen, was diese Befehle eigentlich sind. Ich verrate es: Es sind Funktionen. Eine Funktion ähnelt einer Aktion. Auch eine Funktion hat mehrere Argumente, die ihr in runden Klammern durch Kommata getrennt übergeben werden. show_message etwa entspricht der Aktion "Display a message". Statt der Aktion "Draw Text" mit folgenden Einstellungen
könnten wir auch schreiben
|
GML
|
1
|
draw_text(10, 30, "Das ist ein Text");
|
Eine Funktion kann aber mehr als nur etwas ausführen, nämlich einen Wert zurückgeben. Das macht z.B.
get_integer. Diese Funktion liest eine Zahl ein und gibt sie zurück. Diesen Rückgabewert können wir in eine Variable speichern oder wiederum einer Funktion übergeben. Manche Funktionen führen nichts aus, sondern berechnen nur etwas, z.B.
string. Man merkt im Spiel nicht, dass diese Funktion aufgerufen wurde. Sie wandelt nur eine Zahl in einen String um. Es gibt viele solche Funktionen, die etwas berechnen oder umwandeln (siehe GM-Hilfe Kapitel "Berechnungen"). Ein kurzes Beispiel:
|
GML
|
1
2
3
|
{
show_message( string_repeat("Hello, World!#",3) );
}
|
Beim Testen sehen wir folgende Dialogbox:
Was macht
string_repeat? Wir werfen einen Blick in die Hilfedatei:
string_repeat(str,count) Gibt eine Zeichenkette bestehend aus count Kopien von str wieder. (Deutsche Hilfe, Seite 91)
Das Ergebnis: "Hello, World!#" wird zu "Hello, World!#Hello, World!#Hello, World!#". ( Das Zeichen "#" ist ein Zeilenumbruch)
Kapitel 4: Ablaufkontrolle mit if und else
Uns fehlt noch die Möglichkeit, Bedingungen zu testen. Das geschieht mit dem if-else - Statement. Das if-else - Statement sieht etwa so aus:
|
GML
|
1
2
3
4
5
6
7
8
9
10
11
12
|
if (bedingung)
{
befehl;
befehl;
...
}
else
{
befehl;
befehl;
...
}
|
Die Bedingung in den runden Klammern wird geprüft. Ist sie wahr, werden die Befehle in den geschwungenen Klammern nach if ausgeführt. Ist sie falsch, werden die Befehle in den geschwungenen Klammern nach else ausgeführt. (Normalerweise rückt man den Code in den geschwungenen Klammern um einen Tabstop ein.) Wird else nicht benötigt, kann der komplette else-Zweig weggelassen werden, also so:
|
GML
|
1
2
3
4
5
6
|
if (bedingung)
{
befehl;
befehl;
...
}
|
Sehen wir uns ein Beispiel an:
|
GML
|
1
2
3
4
5
6
7
8
9
10
11
12
|
{
if (secure_mode == true)
{
show_message("Das Spiel läuft im sicheren Modus, es können keine Programme ausgeführt werden!");
}
else
{
show_message("Das Spiel läuft nicht im sicheren Modus");
}
}
|
secure_mode ist eine bereits vorhandene Variable, die angibt, ob das Spiel im sicheren Modus läuft.
true bedeutet "wahr". Der Operator "==" (zwei "="-Zeichen!) prüft zwei Ausdrücke auf Gleichheit. Das ist also eine einfache Überprüfung, ob das Spiel im sicheren Modus läuft. Weitere Vergleiche sind <, >, <=, >= und != (kleiner, größer, kleiner oder gleich, größer oder gleich und ungleich). Der Vergleich mit
true ist aber nicht nötig, da
secure_mode ohnehin nur true oder false (falsch) sein kann. Wir hätten also ohne weiteres schreiben können: if (secure_mode)
Mehrere Bedingungen können mit && (und) und || (oder) verknüpft werden. Ein weiteres Beispiel: Wir wollen überprüfen, ob wir "test.exe" ausführen können. Dafür muss "test.exe" existieren und das Spiel darf nicht im sicheren Modus laufen. Überprüfen, ob eine Datei existiert, kann man mit
file_exists(fname) Gibt zurück, ob die Datei mit dem gegebenen Namen existiert.
Hier der Code:
|
GML
|
1
2
3
4
5
6
7
8
9
10
11
12
|
{
if (secure_mode && file_exists("test.exe"))
{
show_message("test.exe kann ausgeführt werden");
}
else
{
show_message("test.exe kann nicht ausgeführt werden");
}
}
|
Übrigens: Bei nur einem Befehl in einem Zweig können die geschwungenen Klammern weggelassen werden.
Kapitel 5: if und else schachteln
if und else können beliebig geschachtelt werden. Sehen wir uns ein Beispiel an (wie ich bereits erwähnt habe, können bei nur einem Befehl die geschwungenen Klammern weggelassen werden)
|
GML
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
{
var zahl;
zahl = get_integer("Bitte Zahl eingeben",0);
if (zahl >= 1000)
show_message("Die Zahl ist vierstellig oder hat mehr als vier Stellen");
else
if (zahl >= 100)
show_message("Die Zahl ist dreistellig");
else
if (zahl >= 10)
show_message("Die Zahl ist zweistellig");
else
show_message("Die Zahl ist einstellig");
}
|
Hier müssen wir aufpassen! Denn ein else-Statement bezieht sich immer auf das vorherige if-Statement. Folgendes Programm wäre also falsch eingerückt:
|
GML
|
1
2
3
4
5
6
7
|
{
if (bedingung)
if (bedingung2)
befehl;
else
befehl2;
}
|
Hier gehört das else zu dem inneren if! Richtig eingerückt wäre
|
GML
|
1
2
3
4
5
6
7
|
{
if (bedingung)
if (bedingung2)
befehl;
else
befehl2;
}
|
Falls wir es aber möchten, dass das else zum äußeren if gehört, müssen wir Klammern einsetzen.
|
GML
|
1
2
3
4
5
6
7
8
9
|
{
if (bedingung)
{
if (bedingung2)
befehl;
}
else
befehl2;
}
|
Das Beispiel ganz oben sieht noch etwas unschön aus. Da kommt uns zu Hilfe, dass dem Code-Interpreter (das Programm, das unseren Code "durchliest" und ausführt) die Zeilenumbrücke bei if egal sind. So können wir ein if gleich nach dem vorherigen else schreiben und bekommen folgendes:
|
GML
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
{
var zahl;
zahl = get_integer("Bitte Zahl eingeben",0);
if (zahl >= 1000)
show_message("Die Zahl ist vierstellig oder hat mehr als vier Stellen");
else if (zahl >= 100)
show_message("Die Zahl ist dreistellig");
else if (zahl >= 10)
show_message("Die Zahl ist zweistellig");
else
show_message("Die Zahl ist einstellig");
}
|
Das sieht gleich viel übersichtlicher aus!
Kapitel 6: Schleifen
Schleifen werden verwendet, um Anweisungen mehrmals durchzuführen. Die einfachste Schleife ist die while-Schleife. Sie sieht so aus:
|
GML
|
1
2
3
4
5
6
|
while (bedingung)
{
befehl;
befehl;
...
}
|
Die Befehle in den geschwungenen Klammern (bei nur einem Befehl können die Klammern wie bei if weggelassen werden) werden ausgeführt, solange die Bedingung wahr ist. Zählen wir doch einmal bis 10:
|
GML
|
1
2
3
4
5
6
7
8
9
10
11
|
{
var i;
i = 1;
while (i<=10)
{
show_message("i ist jetzt " + string(i));
i = i+1;
}
}
|
Hier setzen wir zunächst die Zähl-Variable i auf 1. Dann führen wir eine Schleife aus, solange i kleiner oder gleich 10 ist. In der Schleife geben wir i aus und erhöhen i um 1.
Für diese häufig verwendete Konstruktion gibt es als Vereinfachung die for-Schleife. Der Code
|
GML
|
1
2
3
4
5
6
|
for (initialisierung; bedingung; weiterzaehlen)
{
befehl;
befehl;
...
}
|
entspricht dem Code
|
GML
|
1
2
3
4
5
6
7
8
|
initialisierung;
while (bedingung)
{
befehl;
befehl;
...
weiterzaehlen;
}
|
Zum Schluss gibt es noch die do-until-Schleife:
|
GML
|
1
2
3
4
5
6
|
do
{
befehl;
befehl;
}
until (bedingung);
|
Hier gibt es zwei Unterschiede zur while-Schleife: Die Befehle werden ausgeführt, bis die Bedingung wahr ist. Außerdem werden die Befehle mindestens einmal ausgeführt. Die do-until-Schleife wird oft verwendet, um Eingaben des Benutzers zu überprüfen, z.B. so:
|
GML
|
1
2
3
4
5
6
7
8
9
10
|
{
var zahl;
do
{
zahl = get_integer("Bitte eine negative Zahl eingeben",-1);
}
until (zahl < 0);
}
|
Zu den Schleifen gibt es noch zwei wichtige Schlüsselwörter:
break und
continue:
break beendet die Ausführung der Schleife.
Mit
continue springt man bis zum Ende der Schleife. Das heißt, die Bedingung wird überprüft und die Schleife gegebenenfalls unterbrochen. Bei einer for-Schleife wird vorher noch "weiterzählen" ausgeführt.
Kapitel 7: Einfachere Fallunterscheidung mit switch
Vergleiche, bei denen eine Variable auf mehrere Werte überprüft wird, kommen sehr oft vor. Ein Beispiel:
|
GML
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
{
var zahl;
zahl = get_integer("Bitte Zahl eingeben",0);
if (zahl == 0)
show_message("Die Zahl ist null");
else if (zahl == 1)
show_message("Die Zahl ist eins");
else if (zahl == 2)
show_message("Die Zahl ist zwei");
else if (zahl == 3)
show_message("Die Zahl ist drei");
else if (zahl == 4 || zahl == 5)
show_message("Die Zahl ist vier oder fünf");
else
show_message("Die Zahl ist kleiner als null oder größer als fünf");
}
|
Dafür gibt es eine Vereinfachung: Das switch-Statement. Der vorherige Code würde bei Verwendung von switch so aussehen:
|
GML
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
switch (zahl)
{
case 0:
show_message("Die Zahl ist null");
break;
case 1:
show_message("Die Zahl ist eins");
break;
case 2:
show_message("Die Zahl ist zwei");
break;
case 3:
show_message("Die Zahl ist drei");
break;
case 4:
case 5:
show_message("Die Zahl ist vier oder fünf");
break;
default:
show_message("Die Zahl ist kleiner als null oder größer als fünf");
break;
}
|
Das funktioniert wie folgt: Der angegebene Ausdruck (in den Klammern nach dem Schlüsselwort
switch) wird ausgewertet. Wenn das Ergebnis in einer der case-Marken vorhanden ist, wird der Code nach dieser Marke bis zum
break ausgeführt. Ist keine solche case-Marke vorhanden, wird der Code nach
default ausgeführt (falls vorhanden). So kann man eine Variable leichter auf verschiedene Werte überprüfen.
Kapitel 8: Arrays
Manchmal möchte man ähnliche Variablen "zusammenfassen". Es ist doch ziemlich unpraktisch, wenn wir z.B. die Farben der Spieler abspeichern wollen, folgendes schreiben müssen:
|
GML
|
1
2
3
4
5
|
color_player1 = c_red;
color_player2 = c_blue;
color_player3 = c_yellow;
color_player4 = c_green;
//usw.
|
Das funktioniert zwar, ist aber recht umständlich. Wenn wir zum Beispiel die Nummer eines Spielers in einer Variable stehen haben (z.B. in "x"), können wir nicht einfach auf color_player"x" zugreifen. Dafür gibt es Arrays.
Ein Array ist eine Liste von Werten, von denen jeder einen Index hat. Das erste Element hat den Index 0. Du kannst dir ein Array wie eine Reihe Schubladen vorstellen, die alle mit Nummern beschriftet sind. Arrays funktionieren folgendermaßen:
|
GML
|
1
2
3
4
5
6
7
8
9
|
{
var array, i, index;
for (i=0; i<20; i+=1) //for-Schleife verwenden, um Elemente 0-19 (beachte das "<"-Zeichen!) zu initialisieren
array[i] = i*2;
index = get_integer("Bitte Zahl von 0 - 19 eingeben",0);
show_message("Das " + string(index) + ". Element des Arrays ist " + string(array[index]));
}
|
Die eckigen Klammern werden verwendet, um auf ein bestimmtes Element eines Arrays zuzugreifen. Mit array[2] etwa greift man auf das Element mit dem Index 2 (eigentlich das 3. Element) zu. Das gerade erstellte Array sieht so aus:
|
Quellcode
|
1
2
|
Index 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Wert 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38
|
Man könnte sich ein Array auch als eine Straße vorstellen. Jedes Haus hat eine Hausnummer. So könnte z.B.
hauptstrasse[54] = "Fam. Müller" sein.
Arrays können auch zweidimensional sein. Darauf will ich noch nicht näher eingehen, ich verrate nur, dass man auf zweidimensionale Arrays mit
array[x,y] zugreifen kann.
Kapitel 9: Skripte - Selbstdefinierte Funktionen
Die eingebauten Funktionen sind, wie wir gesehen haben, recht nützlich. Wir können uns aber auch selbst Funktionen definieren. Dafür gibt es Skripte. Skripte können ganz normal in GML programmiert werden - so wie unser Piece of code, das wir bis jetzt verwendet werden.
Wie wir wissen, kann man einer Funktion Argumente übergeben. Auch ein Skript besitzt Argumente, die in den Variablen argument0, argument1, ... , argument15 abgespeichert sind. (Man kann auch z.B. argument[0] verwenden). Wie Funktionen haben auch Skripte einen Rückgabewert. Diesen Rückgabewert geben wir mit dem return-Statement zurück. Hier ein Beispiel. Erstelle über das Menü Add>Add Skripts ein Skript. Ein Fenster öffnet sich. Ins Feld "Name" oben rechts gibst du "multiplizieren" ein. In dieses Skript fügst du jetzt folgenden Code ein:
|
GML
|
1
2
3
|
{
return argument0 * argument1;
}
|
Dieses Skript übernimmt also zwei Argumente, die es multipliziert und das Ergebnis zurückgibt. Um das Skript zu testen, schreibe folgenden Code (jetzt wieder in unser Space-Event):
|
GML
|
1
2
3
4
5
6
|
{
var zahl;
zahl = multiplizieren(4,2);
show_message("Das Ergebnis ist " + string(zahl));
}
|
Das war alles! Wie du siehst, ist das Programmieren von Skripts nicht sehr schwer. Falls ich dir noch irgendwo in diesem Tutorial ein Skript zeigen will, mache ich das so:
|
GML
|
1
2
3
4
5
|
//Skript addieren
//addiert die ersten beiden Argumente
{
return argument0 + argument1;
}
|
Hier fügst du einfach ein Skript "addieren" hinzu.
Übrigens muss ein Skript nichts zurückgeben - wie etwa dieses hier:
|
GML
|
1
2
3
4
5
6
7
8
|
//Skript show_three_messages
//Gibt die ersten 3 Argumente aus
{
var i;
for (i=0;i<2;i+=1)
show_message(argument[i]);
}
|
Aber Vorsicht: Würden wir schreiben:
x = show_three_messages("a","b","c"); würde das zwar keinen Fehler, aber einen undefinierten Rückgabewert ergeben.
Soll ein Skript beendet werden, ohne etwas zurückzugeben, verwende das Schlüsselwort
exit. Das funktioniert übrigens auch bei einem "Piece of code". Es beendet einfach das Skript bzw. "Piece of code".
Kapitel 10: Operatoren und Ausdrücke
In GML können Ausdrücke zwei verschiedene Typen haben: reelle Zahlen (real values) und Zeichenketten (string values).
Einfache Ausdrücke sind Variablen, Zahlen (Vorsicht: Bei Kommazahlen wird ein Dezimal
punkt verwendet!), Strings mit einfachen oder doppelten Anführungszeichen und vordefinierte Konstanten. Ein paar Beispiele:
|
Quellcode
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
+---------+----------+----------+
|Ausdruck | Wert | Typ |
+---------+----------+----------+
|678.22 | 678,22 | real |
|"Hallo" | "Hallo" | string |
|'Hallo' | "Hallo" | string |
|true | 1 | real |
|pi | 3,141... | real |
+---------+----------+----------+
|var_1 | Wert der | Typ der |
| | Variable | Variable |
| | var_1 | var_1 |
+---------+----------+----------+
|
Mit Operatoren und Klammern kann man Ausdrücke zu weiteren Ausdrücken zusammenfassen (Alle Operatoren siehe weiter unten). Einige Beispiele:
|
Quellcode
|
1
2
3
4
5
6
7
8
9
10
|
+--------------+----------+----------+
|Ausdruck | Wert | Typ |
+--------------+----------+----------+
| 7 * (3+4) | 49 | real |
| "Hal" + 'lo' | "Hallo" | string |
| 45 > 63 | false | real |
| 5 != 2 | true | real |
| true && false| false | real |
|!false | true | real |
+--------------+----------+----------+
|
Operatoren
Folgende Operatoren verknüpfen reelle Zahlen (mathematische Operatoren)
+ (addieren), - (subtrahieren), * (multiplizieren), / (dividieren), div (Ganzzahldivision), mod (Modulo), - (einstellig: Zahl negieren)
div ermittelt das ganzzahlige Ergebnis einer Division, z.B. ist 5 div 2 ist gleich 2
mod ermittelt den Rest einer Division, z.B. 13 mod 10 ist gleich 3
Folgende Operatoren ermitteln Wahrheitswerte (wahr oder falsch):
|
Quellcode
|
1
2
3
4
5
6
7
8
9
10
11
12
|
Operator | Ergebnis
---------+------------------------------------------
a < b | true wenn a kleiner als b
a > b | true wenn a größer als b
a <= b | true wenn a kleiner oder gleich b
a >= b | true wenn a größer oder gleich b
a == b | true wenn a gleich b
a != b | true wenn a ungleich b
a && b | true wenn a und b true
a || b | true wenn a oder b true sind (auch beide)
a ^^ b | true wenn entweder a oder b true sind
!a | true wenn a false
|
Bei Ermittlung von Wahrheitswerten werden Werte <=0 als falsch angenommen, Werte >0 als wahr.
Übrigens gibt es für Anweisungen wie
var_1 = var_1 op var_2 (wobei op ein Operator ist) eine Kurzschreibweise:
var_1 op= var_2 (z.B.
x += 2). Dabei kann man +, -, *, und / verwenden, zusätzlich noch die bitweisen Operatoren, die ich aber erst später erkläre.