272 lines
12 KiB
Text
272 lines
12 KiB
Text
[#_lesson_1_hello]
|
|
== Lektion 1: Hallo
|
|
|
|
image::learn/01-hello.png[Die fertige Lektion, 480]
|
|
|
|
Das hast du am Ende dieser Lektion. Viel her macht es nicht. Es ist aber auch ein Programm,
|
|
das du geschrieben hast, und es läuft auf einer echten Spiel-Engine, und alles andere in diesem
|
|
Buch baut auf den fünf Zeilen auf, die es gemacht haben.
|
|
|
|
[#_set_up_your_work_folder]
|
|
=== Richte deinen Arbeitsordner ein
|
|
|
|
Leg einen Ordner an, in dem du alles aufbewahrst, mit dem Namen `singe` oder wie du willst, und
|
|
leg deine Kopie von Singe hinein. Das ist dein *Arbeitsordner*, und jede Lektion in diesem Buch
|
|
legt darin einen neuen Ordner an. Du legst ihn einmal an und denkst nie wieder darüber nach.
|
|
|
|
Leg im Arbeitsordner einen Ordner namens `hello` an und darin eine Datei namens
|
|
`hello.singe`.
|
|
|
|
[literal]
|
|
----
|
|
singe/ <- dein Arbeitsordner, mit Singe darin
|
|
hello/
|
|
hello.singe
|
|
----
|
|
|
|
Diese Verschachtelung ist keine Pedanterie. Singe behandelt den Ordner, aus dem du es startest,
|
|
als den Ort, an dem die Spiele wohnen, ein Ordner pro Spiel, und es legt dort auch seine eigenen
|
|
Dateien ab. Dein Spiel kommt in einen eigenen Ordner, eine Ebene tiefer. Ab Lektion zwei ist jede
|
|
Lektion ein weiterer Ordner neben `hello`.
|
|
|
|
Zwei Dinge zu den Namen. Am Teil nach dem Punkt, `.singe`, erkennt Singe, dass das ein Skript
|
|
ist. Und auf den meisten Computern, die nicht Windows sind, zählen Großbuchstaben: `Hello.singe`
|
|
und `hello.singe` sind zwei verschiedene Dateien. Gewöhn dir gleich jetzt an, Namen genau so
|
|
zu tippen, wie sie sind.
|
|
|
|
Dass Ordner und Skript denselben Namen haben, ist ebenfalls wichtig, und gleich siehst du,
|
|
warum.
|
|
|
|
Öffne `hello.singe` in deinem Texteditor. Die Datei ist leer. Das ist richtig.
|
|
|
|
[#_type_this_in]
|
|
=== Tipp das ein
|
|
|
|
[source,lua]
|
|
----
|
|
function onOverlayUpdate()
|
|
overlayClear()
|
|
overlayPrint(2, 2, "Hello!")
|
|
return OVERLAY_UPDATED
|
|
end
|
|
----
|
|
|
|
Speichere die Datei.
|
|
|
|
Mach dir noch keine Gedanken darüber, was das alles bedeutet. Zwei Dinge solltest du bemerken,
|
|
bevor du es startest, denn sie gelten für jedes Programm, das du je schreiben wirst.
|
|
|
|
Das erste: Die Leerzeichen am Anfang der mittleren drei Zeilen sind mit Absicht da. Am Programm
|
|
ändern sie überhaupt nichts -- Singe würde es ohne sie genauso ausführen. Aber sie zeigen dir auf
|
|
einen Blick, dass diese drei Zeilen *in* das Ding gehören, das mit `function` anfängt und mit
|
|
`end` aufhört. Das macht jeder Programmierer. Mach es vom ersten Tag an, dann musst du es später
|
|
nie lernen.
|
|
|
|
Das zweite ist die Reihenfolge der Zeilen. Sie passieren von oben nach unten, eine nach der
|
|
anderen, wie die Schritte eines Rezepts. Das ist das Allerwichtigste an einem Programm, und fast
|
|
alles, was Anfänger verwirrt, kommt daher, dass sie es vergessen.
|
|
|
|
[#_run_it]
|
|
=== Starte es
|
|
|
|
Öffne ein Terminal, geh in deinen *Arbeitsordner* -- nicht in den Ordner `hello` darin -- und
|
|
nenne den Ordner, den du starten willst:
|
|
|
|
----
|
|
Singe hello
|
|
----
|
|
|
|
Unter Windows ist das `Singe.exe hello`. Wenn deine Kopie von Singe woanders liegt, tipp den Pfad
|
|
dorthin, etwa `../Singe hello`.
|
|
|
|
Darum haben Ordner und Skript denselben Namen. Bekommt Singe einen Ordner, sucht es darin nach
|
|
einem Skript, das genauso heißt. Nennst du sie verschieden, bekommst du
|
|
`Error: Unable to locate the game.`, und damit sagt die Engine, dass sie nachgesehen und nichts
|
|
gefunden hat, was sie kennt. Du kannst stattdessen auch immer alles ausschreiben, `Singe hello/hello.singe`, und das tut
|
|
dasselbe.
|
|
|
|
Ein schwarzes Fenster geht auf, mit dem Wort `Hello!` nahe der linken oberen Ecke. Drück *Esc*
|
|
oder *Q*, um es zu beenden.
|
|
|
|
Zwei Dinge sind passiert, um die du nicht gebeten hast, und beide sind normal. In deinem
|
|
Arbeitsordner sind neben `hello` Ordner namens `Singe` und `data` aufgetaucht: Im ersten entpackt
|
|
die Engine die Dateien, die sie braucht, im zweiten landet alles, was deine Spiele speichern. Lass
|
|
beide in Ruhe. Und das Fenster ist größtenteils schwarz, weil du außer sechs Zeichen nichts
|
|
gezeichnet hast.
|
|
|
|
[#_what_just_happened]
|
|
=== Was gerade passiert ist
|
|
|
|
Jetzt die fünf Zeilen, eine nach der anderen.
|
|
|
|
[source,lua]
|
|
----
|
|
function onOverlayUpdate()
|
|
----
|
|
|
|
`function` beginnt eine Gruppe von Anweisungen und gibt ihr einen Namen. Alles von hier bis zum
|
|
passenden `end` gehört dazu. Diese hier heißt `onOverlayUpdate`, und die leeren Klammern nach dem
|
|
Namen ergeben in Lektion fünf einen Sinn.
|
|
|
|
Diesen Namen suchst du dir nicht aus. Singe sucht eine Funktion mit genau diesem Namen und ruft sie
|
|
etwa sechzigmal pro Sekunde auf, für immer, damit du Gelegenheit zum Zeichnen hast. Schreibst du
|
|
sie `onoverlayupdate` oder `onOverlayUpdated`, findet Singe sie nicht, dein Fenster bleibt schwarz,
|
|
und nichts sagt dir, dass du einen Fehler gemacht hast. Das ist die erste echte Lektion: Der
|
|
Computer tut, was du geschrieben hast, nicht, was du gemeint hast.
|
|
|
|
Eine Funktion, die du schreibst und die die Engine aufruft, ist ein *Callback*. Du rufst ihn nicht
|
|
selbst auf, und du kannst nicht bestimmen, wann er läuft; du schreibst ihn, und Singe ruft dich
|
|
zurück, wenn es etwas zu tun gibt. Jeder Name in diesem Buch, der mit `on` anfängt, ist ein
|
|
Callback, und es gibt einen für fast alles, was passieren kann: ein gedrückter Knopf, eine
|
|
getippte Taste, ein Sound, der zu Ende ist, die Disc, die einen Frame erreicht. `onOverlayUpdate`
|
|
ist der fürs Zeichnen, und er ist der einzige Ort, an dem gezeichnet werden darf.
|
|
|
|
[source,lua]
|
|
----
|
|
overlayClear()
|
|
----
|
|
|
|
Das ist auch eine Funktion, aber keine, die du schreiben musst: Singe stellt sie bereit, zusammen
|
|
mit mehreren hundert anderen, und du *rufst sie auf*. Eine Funktion aufrufen heißt, ihren Namen
|
|
hinzuschreiben, gefolgt von Klammern. Diese hier wischt den Bildschirm sauber.
|
|
|
|
Sie kommt zuerst, weil Zeichnen nicht ersetzt, was vorher da war, sondern darüber zeichnet. Erst
|
|
löschen, dann zeichnen, jedes Mal.
|
|
|
|
[source,lua]
|
|
----
|
|
overlayPrint(2, 2, "Hello!")
|
|
----
|
|
|
|
Noch ein Aufruf, und diesem muss man drei Dinge sagen, die durch Kommas getrennt in die Klammern
|
|
kommen. Dinge, die du einer Funktion so übergibst, heißen *Argumente*.
|
|
|
|
Die ersten beiden sind Zahlen: wie weit nach rechts und wie weit nach unten. Das dritte ist der
|
|
Text, der ausgegeben werden soll, und er steht in doppelten Anführungszeichen, weil du dem Computer
|
|
so sagst, dass `Hello!` ein Stück Text ist und nicht der Name von irgendetwas. Text in
|
|
Anführungszeichen heißt *String*, und du wirst ständig Strings benutzen.
|
|
|
|
Früher oder später willst du ein doppeltes Anführungszeichen in einem String haben, und der
|
|
naheliegende Weg funktioniert nicht: Das zweite Anführungszeichen beendet den String zu früh, und
|
|
du bekommst denselben Fehler `unfinished string`, den du am Ende dieser Lektion kennenlernst. Es
|
|
gibt zwei Wege drumherum, und es lohnt sich, beide zu kennen.
|
|
|
|
[source,lua]
|
|
----
|
|
overlayPrint(2, 2, "She said \"hello\" to me.")
|
|
overlayPrint(2, 4, 'She said "hello" to me.')
|
|
----
|
|
|
|
Der erste setzt einen Backslash vor jedes Anführungszeichen, das zum Text gehört. Der Backslash
|
|
sagt „das nächste Zeichen gehört zum String, es ist nicht sein Ende“, und das Paar zusammen heißt
|
|
*Escape-Sequenz*. Der zweite benutzt einfache Anführungszeichen, um die Enden des Strings zu
|
|
markieren, und damit bleiben doppelte Anführungszeichen frei für gewöhnlichen Text. Lua nimmt beide
|
|
Arten von Anführungszeichen, solange der String mit derselben Art endet, mit der er angefangen hat.
|
|
|
|
Beide Zeilen geben genau dasselbe aus. Nimm, was leichter zu lesen ist, und das sind meistens die
|
|
einfachen Anführungszeichen, wenn der Text voller doppelter ist.
|
|
|
|
Die beiden Zahlen sind keine Pixel. `overlayPrint` ist der eigene schlichte Text der Engine, und
|
|
er zählt in Zeichenzellen: `(0, 0)` ist ganz oben links in der Ecke, `(1, 0)` ist einen Buchstaben
|
|
weiter rechts und `(0, 1)` eine Zeile tiefer. `(2, 2)` ist also zwei Buchstaben nach rechts und
|
|
zwei Zeilen nach unten. Andere Arten, Text zu zeichnen, zählen sehr wohl in Pixeln, und die lernst
|
|
du in Lektion neunzehn kennen.
|
|
|
|
[source,lua]
|
|
----
|
|
return OVERLAY_UPDATED
|
|
----
|
|
|
|
`return` gibt eine Antwort an den zurück, der die Funktion aufgerufen hat. Singe stellt deinem
|
|
`onOverlayUpdate` bei jedem Aufruf eine Frage: Hast du etwas geändert? `OVERLAY_UPDATED` heißt
|
|
ja, also bring es auf den Bildschirm.
|
|
|
|
`OVERLAY_UPDATED` steht nicht in Anführungszeichen, weil es kein Text ist. Es ist ein Name, den
|
|
Singe schon einem Wert gegeben hat, damit du etwas schreiben kannst, das man lesen kann, statt
|
|
einer Zahl, die du dir merken müsstest.
|
|
|
|
[source,lua]
|
|
----
|
|
end
|
|
----
|
|
|
|
Das passende Ende der `function`. Jede `function` hat genau eins, und es zu vergessen ist der
|
|
häufigste Fehler überhaupt.
|
|
|
|
[#_try_it]
|
|
=== Probier es aus
|
|
|
|
Ändere immer nur eine Sache, speichere und starte es noch einmal. Dieser Kreislauf -- ändern,
|
|
starten, hinsehen -- ist die ganze Arbeit.
|
|
|
|
. *Verschieb den Text.* Ändere `overlayPrint(2, 2, "Hello!")` in
|
|
`overlayPrint(10, 5, "Hello!")`. Dann probier `(0, 0)`. Dann probier `(200, 2)` und finde
|
|
heraus, warum er verschwunden ist.
|
|
. *Sag etwas anderes.* Schreib deinen eigenen Namen zwischen die Anführungszeichen.
|
|
. *Sag zwei Dinge.* Füg unter der ersten eine zweite Zeile mit `overlayPrint` ein, mit einer
|
|
anderen Zeilennummer und anderem Text. Nichts hindert dich daran, so viele zu haben, wie du
|
|
willst.
|
|
. *Nimm das Löschen raus.* Lösch die Zeile `overlayClear()` und starte es. Nichts ändert sich,
|
|
weil sich auf deinem Bildschirm noch nichts bewegt. Setz sie trotzdem wieder ein. In Lektion
|
|
zwei wird sie enorm wichtig, und jetzt ist ein guter Moment, um zu sehen, dass manche Fehler
|
|
warten, bevor sie zubeißen.
|
|
|
|
[#_break_it_on_purpose]
|
|
=== Mach es absichtlich kaputt
|
|
|
|
Anfänger verbringen mehr Zeit damit, Fehlermeldungen zu lesen, als Bücher übers Programmieren,
|
|
also bringen wir die erste gleich hinter uns.
|
|
|
|
Lösch das schließende Anführungszeichen nach `Hello!`, sodass die Zeile
|
|
`overlayPrint(2, 2, "Hello!)` lautet, und starte es. Singe weigert sich zu starten und sagt:
|
|
|
|
----
|
|
Error running script: hello/hello.singe:3: unfinished string near '"Hello!)'
|
|
----
|
|
|
|
Lies die Meldung von links. `hello/hello.singe` ist die Datei: der Ordner, dann das Skript
|
|
darin. `3` ist die Zeile. Der Rest ist, was
|
|
schiefgegangen ist, in diesem Fall ein Stück Text, das geöffnet und nie geschlossen wurde. Geh in
|
|
Zeile drei, setz das Anführungszeichen wieder ein, und es läuft.
|
|
|
|
Fast jeder Fehler, den du je sehen wirst, hat diese Form: eine Datei, eine Zeile und eine
|
|
Beschwerde. Die Beschwerde ist manchmal für jemanden geschrieben, der Lua schon kann, aber Datei
|
|
und Zeile stimmen immer, und in neun von zehn Fällen reicht die Zeile.
|
|
|
|
[#_stop_restarting_it]
|
|
=== Hör auf, es neu zu starten
|
|
|
|
Du hast Singe jetzt fünf- oder sechsmal von Hand gestartet. Lass das. Starte es stattdessen so:
|
|
|
|
----
|
|
Singe -R hello
|
|
----
|
|
|
|
`-R` sagt Singe, dass es dein Skript beobachten soll. Speichere eine Änderung in deinem Editor, und
|
|
das Spiel startet sich einen Moment später selbst neu, mit deiner Änderung darin, ohne das Fenster
|
|
zu schließen. Speicherst du einen Fehler, wird der Fehler ausgegeben, und das Fenster bleibt leer,
|
|
bis du eine Korrektur speicherst.
|
|
|
|
Benutze `-R` für den Rest dieses Buchs.
|
|
|
|
[#_what_you_learned]
|
|
=== Was du gelernt hast
|
|
|
|
* Ein Programm ist eine Datei mit Anweisungen, die der Reihe nach passieren, von oben nach unten.
|
|
* Eine `function` fasst Anweisungen unter einem Namen zusammen und hört bei ihrem `end` auf.
|
|
* `onOverlayUpdate` ist ein Name, nach dem Singe sucht und den es viele Male pro Sekunde aufruft,
|
|
und dort kommt alles hin, was du zeichnest.
|
|
* Eine Funktion aufrufen heißt, ihren Namen und Klammern zu schreiben. Alles in den Klammern ist
|
|
ein Argument.
|
|
* Eine Funktion, die die Engine für dich aufruft, statt dass du sie selbst aufrufst, ist ein
|
|
Callback. `onOverlayUpdate` ist einer.
|
|
* Text in doppelten Anführungszeichen ist ein String.
|
|
* Lösch den Bildschirm, bevor du darauf zeichnest.
|
|
* Ein Fehler nennt dir die Datei und die Zeile. Fang dort an.
|
|
|
|
[#_next_time]
|
|
=== Beim nächsten Mal
|
|
|
|
Dein `Hello!` sitzt, wo du es hingesetzt hast, und bewegt sich nie. Damit es sich bewegt, brauchst
|
|
du einen Ort, an dem eine Zahl liegt, die sich ändert -- eine *Variable*. Darum geht es in der
|
|
ganzen Lektion zwei.
|