Lite-C/Workshop1 01 Funktionen

Aus Scientia
Wechseln zu: Navigation, Suche
Grundlagenkurs Schwierigkeitsgrad Stern.png
Autor Espér
Thematik Lite-C
Vorraussetzungen Programme:
3DGS Trial-Version

Free Edition

Fähigkeiten: keine

Andere Teile

Willkommen zum ersten Teil des Workshops. Ich möchte euch heute ein bisschen über Funktionen erzählen.

Vorbereitung

Da lite-C komplett mit der A7 Engine funktioniert, sollten wir diese auch nutzen. Keine Angst, ihr müsst euch 3D Gamestudio dafür nicht kaufen, es gibt einmal eine 30 Tage trial Version die der Kostenpflichtigen A7 Pro Version entspricht (mit ausnahme das sie keine kompilierten binaries erstellen kann) und einmal die free Version von Lite-C. Die free Version ist die niedrigste Gamestudio Version und unterstützt weder Shader noch hat sie den Welteditor (WED). Die genauen Unterschiede zwischen den Versionen kann man sich hier angucken. Downloadlinks sind in der Tutorialübersicht rechts.

Beiden Versionen liegt die IDE (integrated development environment) SED bei, in denen ihr eure Skripte schreiben könnt (aber nicht müsst!). Weiterhin kann man aus der IDE heraus mit F5 das aktuelle Skript kompilieren und starten lassen.


Funktionen

Funktionen, auch "functions" genannt, dienen als Methodendefinition. Wie in Ruby der "def"-Befehl, so arbeitet auch die functions Syntax in lite-C. Sie wird aufgerufen und arbeitet sich von oben nach unten durch den Code. Um so Abläufe mehrmals durchzuführen, muss man die Funktion natürlich wieder und wieder ausführen.

Legen wir uns doch mal spaßeshalber eine Funktion an.

#include <acknex.h>
#include <default.c>
 
function main()
{
}

Wie ihr seht, startet eine Funktion immer mit dem Wort "function", direkt gefolgt vom Namen der Funktion und einer Klammer, in der man beliebig viele Parameter eintragen kann.
Unsere Funktion hat den Namen main(), die main Funktion ist eine besondere Funktion die beim starten des Programms/Spiels automatisch aufgerufen wird. Alle anderen Funktionen müssen irgendwo anders im Code aufgerufen werden damit ihr Inhalt abgearbeitet wird.
Eine Funktion beginnt immer mit eine { und endet mit einem }, außerhalb dürfen keine Funktions Inhalte stehen.

MERKE: die Funktion namens main startet immer als erstes. Sie ist die Startfunktion des Programms/Spiels.

#include
ist dazu da um bestehende Skripte in das aktuelle Skript einzufügen. Das ist praktisch um Skripte aufzuteilen um nicht ein großes Skript zu haben in dem alles steht. Zum Beispiel kann man seinen KI code in ein extra Skript setzen, den GUI Code wieder in ein anderes usw.

Es gibt zwei verschiedene Arten von #include, einmal kann man den Namen des Skriptes in eckige (<>) Klammern setzen, dass sagt dem Compiler das sich das Skript im "Include" Ordner befindet und einmal kann man den Namen des Skriptes in Anführungszeichen ("") setzen. Letzteres sagt dem Compiler das es das Skript relativ zum Ordner des aktuellen Skriptes suchen soll.

Wir includen zwei Dateien aus dem "Include" Ordner, einmal die "acknex.h" (eine Header Datei) und einmal die "default.c" (eine Skript Datei).
Die acknex.h Datei enthält alle Engine spezifische Funktionen und Structs wie etwa ENTITY oder PANEL.
Die default.c Datei enthält oft benötigte Funktionen wie etwa ein Debug Panel was einem die aktuelle FPS und noch einiges mehr verrät. Die default.c ist allerdings nicht nötig wenn man die Engine nutzen will und sollte spätestens beim finalen release rausgenommen werden damit der Spieler nicht auf alle Funktionen zugreifen kann (diese sind nämlich alle über die F Tasten zu erreichen).

Bauen wir unsere Funktion mal etwas aus:

#include <acknex.h>
#include <default.c>
 
function main()
{
  video_mode = 6;
  level_load(NULL);
  wait(3);
}

Zur Erklärung:

video_mode = 6 <- video_mode ist eine Variable die vor dem ersten Frame der Engine gesetzt werden muss(!). Sie sagt der Engine mit welcher Auflösung sie das Fenster erstellen soll, video_mode = 6 erstellt zum Beispiel ein Fenster der Größe 640x480

level_load(NULL) <- level_load() ist eine Funktion die ein Level (.wmb Dateien) lädt. Den Namen des Level gibt man im Parameter an, level_load("office.wmb") würde also das Level "office.wmb" laden. Wenn man NULL als Parameter übergibt, wird ein leeres Level geladen.

wait(3) <- wait() ist eine der spannendsten Funktionen in Gamestudio. Sie bricht die aktuelle Funktion ab, speichert die lokalen Variablen und die my und you ENTITYs und führt das restliche Skript weiter aus. Nachdem die angegebene Zeit (der erste Parameter) um ist (positive Zahlen stehen für frames, negative für Sekunden), wird die Funktion an der stelle des wait() wieder aufgerufen, die lokalen Variablen wieder hergestellt und die my und you ENTITYs wieder zurückgesetzt. wait() ist so extrem einfach wie auch kompliziert, dass gerade Anfänger gerne Fehler beim nutzen dieser Funktion begehen.

Sehr wichtig, eine Codezeile muss immer mit einem Semikolon ( ; ) enden, kann sich aber dafür über mehrere Zeilen erstrecken.


Nun denn. Wir haben eine Startfunktion. Wie aber rufen wir eine weitere Funktion auf? Dies ist ganz einfach. Erstellt eine weitere Funktion ÜBER der main() Funktion. Das hat den Grund, dass der Compiler von oben nach unten das Skript durcharbeitet. Dinge die über anderen stehen, haben daher keine Ahnungen von dem was unter ihnen noch kommt.

#include <acknex.h>
#include <default.c>
 
 
function test()
{
  beep();
}
 
function main()
{
  video_mode = 6;
  level_load(NULL);
  wait(3);
}

Also bitte niemals die aufzurufenden Funktionen UNTER der main() Funktion setzten, da Lite-C nur die Methoden kennt, die bis zur aktuellen Codezeile gelesen wurden. Der Ruby-Interpreter hingegen interessiert sich nicht für die Reihenfolge, sondern sucht - wenn auch vergebens - nach der genannten Methode. Wenn ihr allerdings doch einmal eine Funktion aufrufen müsst die in der aufzurufenden Funktion noch nicht bekannt ist, müssen Funktions Prototypen verwendet werden. Ein Funktions Prototyp ist im Grunde nur die Kopfzeile der Funktion, also der Teil der nicht in { und } steht, gefolgt von einem Semikolon. Ein Funktions Prototyp darf übrigens nicht in einer anderen Funktion stehen, sondern außerhalb einer. "Sieht" der Compiler nun so einen Prototypen, weiß er das diese Funktion weiter unten noch existiert und erlaubt den Zugriff auf diese Funktion auch von Funktionen aus denen die Funktion noch nicht bekannt ist.

beep() <- beeb() ist eine Funktion, die einen Fehlersound abspielt und 100ms wartet bevor das Spiel fortgesetzt wird.

Nun rufen wir die Testfunktion mal auf.

#include <acknex.h>
#include <default.c>
 
 
function test()
{
  beep();
}
 
function main()
{
  video_mode = 6;
  level_load(NULL);
  wait(3);
 
  test(); // Diese Zeile ruft die test() Funktion auf!
}

Startet nun das Script mit dem Testbutton Testbutton lite-c.jpg. Ihr werdet nach dem Laden des Spiels kurz einen Fehlersound hören.

Glückwunsch. Ihr habt eine Funktion geschrieben und gestartet.

Hier nochmals die Syntax einer Funktion (Parameter sind optional):

function name(parameter1,parameter2,parameter3, ...eter5){inhalt1; inhalt2; inhalt3; ...}

Aktionen

Der größte Unterschied zwischen Funktionen und Aktionen ist, das ihr Aktionen einem Model direkt im Level-Editor geben könnt. Solltet ihr ein Model in das Level laden ( über den WED, Welt Editor ), so werden diesem nur Aktionen, jedoch keine Funktionen in der Startaktionen Liste angezeigt.

Aktionen werden hauptsächlich verwendet, um einem Model eine Startaktion in der Welt zu geben. Das Model, wir nennen es ab jetzt Entitie, startet diese Aktion sofort zu Levelbeginn. Daher kommen in Aktionen normalerweise Bewegungen, Handlungsabläufe und sonst alles was zum Levelstart da sein muss.

Eine Aktion lässt sich erstellen wie eine Funktion:

action testaktion()
{
   // Inhalt
}

Wie ihr seht, ist die größte Änderung der Name. "function" wird zu "action". Zudem besitzen Aktionen keine Startparameter. Und man kann auch keine Werte per return zurückgeben.

Hier nochmals die Syntax einer Aktion:

action testaktion(){Inhalt1; inhalt2; inalt3; ...}

Void

Voids sind besondere Formen der Funktionen. Sie lassen sich wie Funktionen erstellen:

#include <acknex.h>
#include <default.c>
 
 
void test()
{
  beep();
}
 
void main()
{
  video_mode = 6;
  level_load(NULL);
  wait(3);
 
  test(); // Diese Zeile ruft die test() Funktion auf!
}

simpel nicht. dann versucht mal das!

#include <acknex.h>
#include <default.c>
 
 
void test()
{
  beep();
  return(1);
}
 
void main()
{
  video_mode = 6;
  level_load(NULL);
  wait(3);
 
  var over = 0;
  over = test(); // Diese Zeile ruft die test() Funktion auf!
  if(over == 1)
  {
   error("Wert ist 1");
  }
}

Normalwerweise sollte error euch hier nun die Nachricht ausgeben, das der Wert von over 1 beträgt. Das tut er aber nicht! Grund ist, das Voids keine Werte zurückgeben können - over bleibt also 0. Voids sollten daher für Funktionen verwendet werden, die nicht zur Berechnung und Rückgabe von Werten bestimmt sind.

WICHTIG: Obwohl Voids nichts zurückgeben können, sind sie NICHT schneller als andere Funktionen! Dieses Gerücht ist frei erfunden!


Andere Funktionstypen

function, action und void sind nicht die einzigen wege eine Funktion zu erstellen. Es gehen auch Pointerbasierte Funktionstypen wie:

var name(parameter)
{  }
 
STRING* name(parameter)
{  }

oder auch C bekannte Typen:

int name(parameter)
{  }
 
static name(parameter)
{  }

Bevorzugt werden aber die Basisfunktionen verwendet.