1 Das Lua-Tutorial - #2 Tipps und Bibliotheken
Im ersten Teil wurde geklärt, was eine Skriptsprache ist, welchen Editor man für die Bearbeitung nehmen sollte.
Es wurden die wichtigsten Sprachelemente wie Variablen, If-Else-Verzweigungen, Schleifen, Funktionen und Tabellen vorgestellt.
Am Ende dieses Teils beherrscht man folgende Grundlagen
- Lösen von Programmierfehlern
- Zurechtfinden im offiziellen Lua-Handbuch
- Vereinfachen gängiger Arbeiten
- Projektorganisation und Teamarbeit
- Allgemeine Funktionen und die math-Bibliothek
1.1 Lösen von Programmierfehlern
Die meisten Fehlerquellen kennt jeder:
- Tippfehler
- Nichtbeachtung der Großschreibung
- Übergabe eines fehlerhaften Parameters
- Illegaler Aufruf einer Funktion
- Zugriff auf eine Variable, die nicht existiert
- Logikfehler
Doch wenn diese Fehler auftreten, können besonders die letzteren viel Suchzeit in Arbeit beanspruchen, sogar einige Tage.
Tippfehler sind schnell zu finden: Stimmt der Quellcode nicht,
meldet der Interpreter, dass etwas nicht stimmt und benennt
möglichst genau die betroffene Stelle. Dazu klickt im
Dialog der Fehlermeldung auf „Show Bug Report“ und findet Informationen
zum System, den Prozessen und vor allem: Welcher Fehler eingetreten ist
(und eventuell warum) – Rot eingerahmt.
In diesem Fall habe ich zum Beispiel statt „function“ „funktion“
für einen Funktionsrumpf geschrieben (Zeile 20).
Man sieht: Diese Fehlerquelle ist leicht zu finden und schnell zu lösen.
Die Nichtbeachtung der Groß- und Kleinschreibung führt schnell zu
fehlerhaften Funktionsnamen und Variablen.
Hier helfen nur drei Methoden:
- Folge dem Quellcode und schaue nach,
ob irgendetwas falsch geschrieben wurde. - Halte dazu beim Programmieren für Funktionen, Variablen
und Tabellen eine einheitliche Namensgebung ein (siehe unten). - Untersuche Argumente und Variablen mit einer
selbstgebauten DebugMessage()-Funktion (siehe Teil 1).
Die Prüfung eines fehlerhaften Arguments kann schnell erledigt sein. Dazu gibt es folgende Möglichkeiten:
- Gib mit der Funktion DebugMessage() eine Informationsnachricht mit den Parametern aus.
- Prüfe die Werte auf Gültigkeit, indem der Wertebereich überprüft wird. Überprüfe Variablen auch auf den Wert nil.
Ein illegaler Aufruf einer Funktion kann einen fatalen Fehler auslösen: Solche Fehler lösen einen zwanghaften Fehlerdialog aus.
Bekommt eine Funktion Argumente, die außerhalb des zu erwartenden Wertebereiches sind, kann sie entweder Fehlermeldungen ausgebenoder löst ein unvorhergesehenes Verhalten aus.
Möchte man prüfen, ob eine Variable ungültig ist, muss man Sie nur gegen den Wertebereich oder nil prüfen.
Da es allerdings viele Variablen im Quellcode gibt, ist dieses Verfahren sehr aufwendig.
Logikfehler lösen ein unerwartetes Programmverhalten aus. Hier gibt es nur eine Lösung:
- Verhalten beobachten, schildern
- Auf dem Papier das Ganze an einem Worst-Case-Szenario durcharbeiten.
Tipp: Mit Microsoft® Visio® kann man die Logik an Flussdiagrammen visualisieren. - Logik verändern und beobachten was passiert
Fehler sind vielschichtig, doch für alles gibt es eine Lösung. Es hilft auch, wenn man seine persönlichen Lieblingsfehler kennt,
denn dann weiß man, wo man anfangen soll!
Eine weitere Lösung kann das Kommentieren sein: Diese Kommunikationsaufgabe hilft Fehler schneller zu verstehen!
1.2 Das Lua-Handbuch
Zur Skriptsprache Lua gibt es ein offizielles Handbuch. Das Handbuch ist im Dokumentationsbereich auf der offiziellen Seite
von Lua (http://www.lua.org) zu finden. Der Direktlink zum Lua 5.1 Reference Manual lautet http://www.lua.org/manual/5.1/.
Es ist zu beachten, dass das Handbuch nur auf Englisch verfügbar ist nur alle Funktionen allgemein schreibt.
Teilweise ist Vorwissen aus anderen Programmiersprachen notwendig, um die Hintergründe zu verstehen.
Für uns sind die Punkte „2 - The Language“ und „5 - Standard Libraries“ wichtig. Die restlichen Punkte sind für
Anwendungsprogrammierer oder Sprachentwickler wichtig.
Die Sprachdokumentation ist allgemein als Text mit Beispielen beschrieben.
Die Funktionen werden mit der Definition vorgestellt, was diese Bewirkt und deren Voraussetzungen,
die Bedeutung der Parameter und der Wertebereich der Argumente sowie die zu erwartenden Rückgabewerte.
Die Inhaltsübersicht auf der Hauptseite listet die einzelnen Themen auf. Scrollt man auf der Hauptseite herunter,
sind alle eigenen Lua-Funktionen aufgelistet. Auf der rechten Spalte findet man unter dem Namen „C API“ und „auxilary library“
eine Dokumentation für Anwendungsprogrammierer.
1.3 Vereinfachen gängiger Arbeiten
Dieser Abschnitt hätte auch den Titel „Tipps zum Überleben“ bekommen können. Und darum geht es jetzt: Tipps,
damit das Chaos vermieden wird. Die Funktion von Kommentaren wurde im ersten Teil geklärt.
Dennoch gibt es viele Fragen, wie man diese sinnvoll anwendet. Es folgen ein paar Tipps und Vorlagen:
1.3.1 Informationen zur Datei
Wie beschrieben sollten hier Informationen stehen, was die Datei allgemein macht, wer sie geschrieben hat,
wann sie geschrieben und was an ihr verändert wurde. Zusatzinformationen wie die verwendete Lizenz,
die Dokumentationssprache und der Name der Gruppe tragen ebenfalls zur Übersicht bei und sind teilweise Pflicht.
Das folgende Bild zeigt eine exemplarische Dokumentation (ohne Lizenz):
1.3.2 Beschreibung globaler Definitionen
Im Grunde müssen globale Definitionen nur beschreiben, was diese bedeuten. Beispiel:
1.3.3 Funktionen
Funktionen benötigen ebenfalls eine Dokumentation (ganz wichtig!):
- Was macht die Funktion?
- Welche Parameter gibt es? Welche Argumente sind gültig? Abhängigkeiten?
- Welche Rückgabewerte sind zu erwarten? Fehlerbehandlung?
Hierzu ein Beispiel:
Aufgabenfelder / Programmblöcke / Rümpfe
Hier sollte man nicht beschreiben, was die Sprachkonstrukte machen, sondern warum bestimmte Prozesse notwendig sind und wie diese
zusammenhängen. Diese Dokumentation sollte den Logikprozess abbilden – und nicht die Sprache. Ein Beispiel siehe oben bei den Funktionen
(Warum interessiert mich die Nachricht überhaupt nicht?).
Wie benennt man Variablen und Funktionen?
Nach dem vereinbarten Muster der Gruppe. Doch es gibt Grundregeln: PascalCase oder camelCase.
Globale Definitionen werden oftmals KOMPLETT_GROSS und mit Unterstrich in Worte getrennt:
pxpPZB_MGN_2000 ist so ein Beispiel. Der Name der Gruppe (pxp) wurde hier vor der Definition klein angefügt.
Ansonsten schreibe ich Variablen im PascalCase: TrainSpeed.
Globalen Variablen bekommen bei mir ein sogenanntes Prefix: g_. Damit ist eindeutig, dass diese Variable global ist.
Beispiel: g_TrainData.
Wenn möglich, schreibe ich Funktionen im camelCase. Damit die Funktionsgruppe erkennbar ist,
nutze ich ein Verb: create, release, get, set, isoder has. Beispiel:
function isPZBContactActive()
Das ganze Prinzip wird in den folgenden Beispielprojekten (z.B. Signale) sicherlich klar.
1.4 Projektorganisation und Teamarbeit
Projekte sollten einen eigenen Ordner bekommen, wenn dies möglich ist.
Alle Dateien eines Projekts sollten in einem eigenen Ordner nach Funktion sortiert werden. Z.B.
- Objekte in „Meshes“
- Texturen in „Textures“
- Vorlagen in „Templates“
- Skripte in „Scripts“
- Grundmodelle in „Template Meshes“
Wichtig ist, dass man alle Daten einer bestimmten Version zuordnen kann, damit der Austausch der Dateien einfach vonstattengeht.
Die Versionsverwaltung kann man für relativ kleine Projekte manuell selbst auf einem Downloadserver durchführen.
Ein paar FTP-Accounts und schon kann das Austauschen beginnen. Eine Teamplattform ist natürlich von Vorteil.
Für größere Projekte, an denen viele Mitarbeiter (ab 5) arbeiten, empfiehlt es sich eine Software zu nehmen
und einen Online-Server, auf denen man mit wenigen Klicks alles austauschen kann.
Eine solche Software ist zum Beispiel TortoiseSVN mit einem passenden Onlinespeicher. Dieses Verfahren
wird in einem anderen Tutorial beschrieben, da es den Rahmen dieses Tutorials sprengen würde.
Wichtig ist: Sei auf den neuesten Stand und gib Bescheid, wenn Daten aktualisiert worden sind.
Und dokumentiere was wo wann geändert wurde.
1.5 Die Mathematikbibliothek
Dieser Abschnitt stellt die wichtigsten Funktionen und die Mathematikbibliothek vor.
1.5.1 Allgemeine Funktionen
assert(value[, message]) | Löst einen fatalen Fehler aus, wenn value false oder nil ist. Gibt die Nachricht message aus. |
dofile(filepath) | Führt eine Datei aus. Der Arbeitsordner (Current Working Directory) bezieht sich auf den Ordner, in dem sich die RailWorks.exe befindet. |
print(text) | Schreibt text in die Konsole bzw. schreibt ihn in die Log-Datei. |
tonumber(text) | Wandelt text in eine Zahl um. Gibt nil zurück, wenn es nicht funktionierte. |
tostring(value) | Wandelt einen Wert in einen Text um. |
type(value) | Gibt den Datentyp von value in Textform zurück: „nil“, „number“, „string“, „table“, „function“, „boolean“, „thread“ oder „userdata“. |
key, value = inpairs(table) | Für die generische For-Schleife. Iteriert eine Tabelle. key besitzt den Namen des Schlüssels, value den Wert. |
1.5.2 Die Bibliothek math
Der Zugriff auf die math-Bibliothek erfolgt über die Tabelle math: math.(Funktion). Die wichtigsten Funktionen sind fett markiert.
abs(x) | Gibt den absoluten Wert von x zurück. |
acos(x) | Gibt den Arkuscosinus von x in Radiant zurück. |
asin(x) | Gibt den Arkussinus von x in Radiant zurück. |
atan(x) | Gibt den Arkustangens von x in Radiant zurück. |
ceil(x) | Rundet die Zahl x auf (Ganzzahl). |
cos(x) | Berechnet den Cosinus von x in Radiant. |
cosh(x) | Berechnet den Cosinus Hyperbolicus von x in Radiant. |
deg(radian) | Berechnet den Winkel in Grad von radian. |
exp(x) | Berechnet e^x. |
floor(x) | Rundet x ab. |
fmod(x, y) | Gibt den Rest der Division x/y zurück. Er ist zwischen [0 und < y). |
frexp(x) | Gibt m und e so zurück, dass x = m * pow(2, e) gilt. e ist eine Ganzzahl und der absolute Wert von m ist zwischen [0.5 und 1) – oder 0 wenn x 0 ist. |
huge (Variable) | Der größte anzunehmende Zahlenwert. |
ldexp(m, e) | Berechnet m * exp(x). |
log(x) | Berechnet den natürlichen Logarithmus 〖log〗_e (x). |
log10(x) | Berechnet 〖log〗_10 (x). |
max(a, b, …) | Gibt den größten aller Werte zurück. |
min(a, b, …) | Gibt den kleinsten aller Werte zurück. |
modf(x) | Gibt zwei Werte von x zurück: Den Integralen (Ganzzahl) und den Rationalen. |
pi (Variable) | π (3.1415…) |
pow(x, n) | Berechnet x^n. |
rad(degree) | Wandelt einen Winkel von Grad nach Radiant um. |
random([max[, min]) |
Berechnet eine Zufallszahl. Wenn max angegeben ist, ist der Wert zwischen 0 bis max. Wenn min angegeben ist, zwischen min bis max. Ansonsten von 0 bis 1. |
randomseed(x) | Setzt den Seed des Zufallsgenerators. |
sin(x) | Berechnet den Sinus von x in Radiant. |
sinh(x) | Berechnet den Sinus Hyperbolicus von x in Radiant. |
sqrt(x) | Wurzel aus x. x muss größer als 0 sein. |
tan(x) | Berechnet den Tangens von x in Radiant. |
tanh(x) | Berechnet den Tangens Hyperbolicus von x in Radiant. |
1.6 Zusammenfassung des Teils
In diesem Teil wurden Programmiertipps gegeben. Zudem wurden die wichtigsten Funktionen und die Mathematikbibliothek vorgestellt.
Es gibt noch weitere Bibliotheken: Dazu ist ein Blick in die offizielle Dokumentation unverzichtbar: „5 - Standard Libraries“.
Im nächsten Teil werden Besonderheiten bezüglich RailWorks 3 vorgestellt.