/docs/sdk/script/Funcs.xml
https://bitbucket.org/randrian/openclonk2 · XML · 189 lines · 144 code · 45 blank · 0 comment · 0 complexity · 370cdcc0e8e83f90c3f24cd79eb1fc60 MD5 · raw file
- <?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
- <!DOCTYPE doc SYSTEM "../../clonk.dtd">
- <?xml-stylesheet type="text/xsl" href="../../clonk.xsl"?>
- <doc>
- <title>Funktionen</title>
- <h>Funktionen</h>
- <part>
- <text>Eine Skriptfunktion fasst ein bestimmtes Stück Code zusammen, welches dann von der Engine oder einem anderen Script aufgerufen (also ausgeführt) werden kann. Das gesamte Script eines Szenarios oder Objekts ist in solchen Funktionen untergebracht.</text>
-
- <h>Parameter und Rückgabewerte</h>
- <text>An eine Scriptfunktion können beim Aufruf bis zu 10 Parameter übergeben werden. Diese können dann innerhalb der Funktion ausgewertet und verwendet werden. Beim Beenden kann eine Funktion mit dem Befehl <funclink>return</funclink>() einen einzelnen Wert an den Aufrufer zurückgeben.</text>
-
- <h>Syntax</h>
- <text>Eine einfache Funktionsdeklaration könnte z.B. folgendermaßen aussehen:</text>
-
- <code>func MeineFunktion()
- {
- <funclink>Message</funclink>("Meine Funktion wurde aufgerufen!");
- }</code>
- <text>Das Script der Funktion steht in einem sogenannten Block (der Bereich zwischen '{' und '}' ). Vor diesem Block steht die <strong>Funktionsdeklaration</strong>. Diese beginnt mit "<code>func</code>", gefolgt von dem <strong>Funktionsnamen</strong> (hier: "MeineFunktion"). In der Klammer nach dem Funktionsnamen können Funktionsparameter definiert werden (s.u.).</text>
- <text>Beim Aufruf der Funktion wird die Meldung "Meine Funktion wurde aufgerufen!" ausgegeben.</text>
-
- <code>func ZeigeZahl(int zahl)
- {
- <funclink>Message</funclink>("ZeigeZahl wurde mit dem Parameter %d aufgerufen!", 0, zahl);
- }</code>
-
- <text>Hier wurde ein Parameter mit dem Namen "zahl" vom Typ "int" definiert. Wird beim Aufruf der Funktion ein Parameter angegeben, so erhält "zahl" den übergebenen Wert. Der übergebene Wert wird in diesem Fall in einer entsprechenden Nachricht ausgegeben.</text>
- <text>Ein Aufruf der Funktion "ZeigeZahl" könnte z.B. folgendermaßen aussehen:</text>
-
- <code>ZeigeZahl(42)</code>
-
- <text>Durch die Ausführung dieses Aufrufes würde die Nachricht "ZeigeZahl wurde mit dem Parameter 42 aufgerufen!" erzeugt.</text>
- <text>Es ist auch erlaubt, mehrere Parameter zu definieren. Dazu ein Beispiel:</text>
-
- <code>func ZeigeSumme(zahl1, zahl2, zahl3, zahl4)
- {
- <funclink>Message</funclink>("Die Summe der ersten 4 Parameter ist %d.", 0, zahl1 + zahl2 + zahl3 + zahl4);
- }</code>
-
- <text>In diesem Fall wurden vier Parameter benannt. Ihnen wurden die Namen "zahl1" bis "zahl4" zugewiesen (die Parameternamen werden jeweils durch Kommata getrennt).</text>
- <text>In der Nachricht wird dann jeweils die Summe der vier übergebenen Zahlen ausgegeben. Der Funktionsaufruf</text>
-
- <code>ZeigeSumme(1, 2, 3, 4);</code>
-
- <text>führt also zu der Nachricht "Die Summe der ersten 4 Parameter ist 10.".</text>
-
- <h id="parametertypen">Parametertypen</h>
- <text>Es ist möglich, den Typ der ürgebenen Parameter zu erzwingen. Dazu einfach den Namen des entsprechenden Typs (siehe dazu <emlink href="script/Typechecks.html">Typechecks</emlink>) vor den Parameternamen setzen:</text>
-
- <code>func TypParameterFunktion(object clnk, id def, int anzahl, string msg)
- {
- <emlink href="for.html">for</emlink>(var i = 0; i < anzahl; i++)
- <funclink>CreateContents</funclink>(def, clnk);
- <funclink>Message</funclink>(msg, clnk);
- }</code>
-
- <text>Diese Funktion gibt einem übergebenen Clonk (<code>clnk</code>) eine Anzahl von Objekten (<code>anzahl</code>) vom angegebenen Typ (<code>def</code>) und gibt die Nachricht <code>msg</code> über seinem Kopf aus. Dabei wird bei Funktionsaufruf sichergestellt, dass alle Parameter in den angegebenen Typ konvertiert werden, sofern dies möglich ist (siehe <emlink href="script/Typechecks.html">Typechecks</emlink>).</text>
- <text>Ein Aufruf wie <code>TypParameterFunktion(1, CLNK, "Text", 5)</code> hier würde also einen Typfehler auslösen.</text>
-
- <h id="spezial">Vorgabeparameter</h>
- <text>Anders als in verschiedenen anderen Programmiersprachen ist es in C4Script immer erlaubt, weniger oder auch mehr Parameter zu übergeben als in der Funktion definiert (maximal jedoch 10).</text>
- <text>Die folgenden Aufrufe der obigen Funktion sind also völlig legal:</text>
-
- <code>ZeigeSumme(1, 2);
- ZeigeSumme(1, 2, 3, 4, 5, 6);</code>
-
- <text>Beim ersten Aufruf wurden weniger Parameter übergeben, als in ZeigeSumme deklariert wurden. Die "überschüssigen" Parameter in ZeigeSumme erhalten nun einfach den Wert 0, die Meldung lautet entsprechend "Die Summe der ersten 4 Parameter ist 3." (=1+2+0+0).</text>
- <text>Wird ein Parameter nicht übergeben, so ist das mit der Übergabe von 0 identisch.</text>
- <text>Umgekehrt wurden beim zweiten Aufruf <em>mehr</em> Parameter übergeben als in ZeigeSumme definiert wurden. Der 5. und 6. Parameter werden nun keinem Parameter in ZeigeSumme zugewiesen. Sie können allerdings noch mittels der Funktion <funclink>Par</funclink>() abgefragt werden [Par(4) und Par(5)]. Da die letzten beiden Parameter von ZeigeSumme also nicht ausgewertet werden können, wird hier "nur" die Meldung "Die Summe der ersten 4 Parameter ist 10." ausgegeben.</text>
-
- <h>Rückgabewerte</h>
- <part>
- <text>Jede Scriptfunktion gibt einen Rückgabewert an den Aufrufer (die aufrufende Funktion bzw. die Engine) zurück. Dieser Wert wird im Script mittels <funclink>return</funclink>() gesetzt.</text>
- <h>Beispiel:</h>
-
- <code>func Differenz(Zahl1, Zahl2)
- {
- <funclink>return</funclink>(<funclink>Abs</funclink>(Zahl1 - Zahl2));
- }</code>
-
- <text>Hier wird die Differenz der ersten beiden Parameter bestimmt und das Ergebnis "zurückgegeben". Eine andere Scriptfunktion könnte nun diese Funktion benutzen, um die Differenz zweier Zahlen zu berechnen:</text>
-
- <code>func ZeigeDifferenz(Zahl1, Zahl2)
- {
- <funclink>Message</funclink>("Die Differenz zwischen %d und %d beträgt %d!", Zahl1, Zahl2, Differenz(Zahl1, Zahl2));
- } </code>
-
- <text>Der Aufruf "ZeigeDifferenz(5, 2)" würde also die Meldung "Die Differenz zwischen 5 und 2 beträgt 3!" erzeugen.</text>
- </part>
-
- <h id="Aufrufb">Aufrufberechtigungen</h>
- <part>
- <text>Für jede Funktion kann eine "Aufrufberechtigung" festgelegt werden, die bestimmt, von wo die Funktion aufgerufen werden darf. Hier gibt es drei verschiedene Stufen:</text>
-
- <text><table>
- <row>
- <col><code>public</code></col>
- <col>darf von überall aufgerufen werden, auch aus anderen Scripten (Standard)</col>
- </row>
- <row>
- <col><code>protected</code></col>
- <col>darf nur aus dem eigenen Script und von der Engine aufgerufen werden</col>
- </row>
- <row>
- <col><code>private</code></col>
- <col>darf nur aus dem eigenen Script aufgerufen werden</col>
- </row>
- </table></text>
-
- <text>Die Aufrufberechtigung wird jeweils direkt vor "<code>func</code>" geschrieben:</text>
-
- <code>private func PrivateFunktion()
- {
- // Diese Funktion darf nur im selben Script aufgerufen werden!
- }</code>
-
- <text>Wird diese Funktion nun von einem externen Script aufgerufen, so gibt die Engine einen Fehler aus.</text>
-
- <h>Anmerkung</h>
- <text>Da es in manchen Fällen notwendig erscheint, auch eine geschützte Funktion aus einem fremden Script aufzurufen, kann die Aufrufbeschränkung mittels <funclink>PrivateCall</funclink> bzw. <funclink>ProtectedCall</funclink> umgangen werden.</text>
- </part>
-
- <h>Globale Funktionen</h>
- <part>
- <text>Eine Funktion wird <code>global</code> definiert, indem "<code>global</code>" vor "<code>func</code>" gestellt wird.</text>
- <text>Eine als <code>global</code> definierte Funktion kann aus jedem Script aufgerufen werden. Ihr Gültigkeitsbereich stimmt mit denen der Engine-Funktionen überein. Sie können damit auch verwendet werden, um Engine-Funktionen zu überladen und ihre Funktionsweise zu modifizieren.</text>
-
- <h>Beispiel:</h>
- <code>global func CreateContents(id, pObj, iCnt)
- {
- var pObj;
- <emlink href="for.html">for</emlink>(var i = 0; i < iCnt; i++)
- pObj = <funclink>inherited</funclink>(id, pObj);
- return(pObj);
- }</code>
-
- <text>Definiert die Engine-Funktion <funclink>CreateContents</funclink> neu. Dabei wird sie um einen neuen Parameter iCnt erweitert, der es erlaubt, mehrere Objekte zu erzeugen. Man beachte, dass <funclink>inherited</funclink> innerhalb dieser Funktion der durch sie überladenen Engine-Funktion <funclink>CreateContents</funclink> entspricht!</text>
-
- <h>Achtung!</h>
- <text>Eine globale Skriptfunktion übernimmt den Kontext der aufrufenden Funktion. Das bedeutet insbesondere, dass <funclink>this</funclink>() das aufrufende Objekt ist (natürlich nur, wenn die Funktion auch aus einem Objekt aufgerufen wurde). Deshalb darf in einer globalen Funktion auch keine objektlokale Variable verwendet werden und auch keine andere lokale Funktion aufgerufen oder lokale Variablen verändert werden! Das folgende Objektscript ist also nicht zulässig:</text>
-
- <code>local iNummer;
- func ObjektFunktion()
- {
- <funclink>Log</funclink>("ObjectFunktion: local iNummer hat den Wert %d!", iNummer);
- }
- global func GlobaleFunktion()
- {
- ObjectFunktion(); // Fehler!
- iNummer++; // Fehler!
- }</code>
-
- <text>Beide Versuche, auf objektlokale Elemente zuzugreifen, schlagen fehl. Das ist leicht einsichtig, da GlobalFunktion() ja aus jedem beliebigen Script, also auch aus dem Szenarioscript oder aus dem Script eines anderen Objekts aufgerufen werden kann. Dieses Script besitzt dann höchstwahrscheinlich auch keine Variable "iNummer" oder Funktion "ObjectFunktion", die benutzt werden könnte. Da die Engine dies beim Vararbeiten des Scripts nicht sicherstellen kann, wird ein Fehler ausgelöst.</text>
- <text>Anmerkung: Soll eine lokale Funktion in einem fremden Kontext aufgerufen werden, so sollte "<code>this()->Funktion(...)</code>" verwendet werden. Dann gibt es nur einen Fehler, wenn die Funktion auch wirklich nicht im Objekt existiert.</text>
- </part>
-
- <h id="referenzen">Referenzen</h>
- <text>Manchmal ist es nötig, nicht den Wert einer Variable zu übergeben, sondern einen "Verweis" auf die Variable selbst.</text>
- <text>Nehmen wir an, wir wollen eine Funktion schreiben, die die Position des höchstrangingsten Clonks eines Spielers zurückgibt. Dies ist normalerweise ohne Tricks nicht möglich, da eine Funktion ja nur einen Rückgabewert hat (die Position hat aber zwei Komponeneten: X und Y).</text>
- <text>Doch durch Verwendung von Referenzübergabe als Parameter kann hier die Information trotzdem zurückgegeben werden:</text>
-
- <code>func GetHiRankPosition(int plr, &x, &y)
- {
- var oHiRank = <funclink>GetHiRank</funclink>(plr);
- x = <funclink>GetX</funclink>(oHiRank);
- y = <funclink>GetY</funclink>(oHiRank);
- }
- func Aufruf()
- {
- var iHiRankX, iHiRankY;
- GetHiRankPosition(0, iHiRankX, iHiRankY);
- <funclink>Message</funclink>("Die Position des HiRanks von Spieler 1 ist: %d/%d", 0, iHiRankX, iHiRankY);
- }</code>
-
- <text><code>x</code> und <code>y</code> in <code>GetHiRankPosition</code> sind hier <strong>Referenzen</strong>, d.h. wenn man sie setzt, werden die Variablen gesetzt, die man als Parameter angab (in diesem Fall <code>iHiRankX</code> und <code>iHiRankY</code>). Man beachte, dass ein Aufruf wie "<code>GetHiRankPosition(0, iHiRankX + 1, iHiRankY)</code>" zu einem Typfehler führt, da "<code>iHiRankX + 1</code>" keine Variable, sondern eine Zahl ist!</text>
- <text>Man beachte, dass "<code>&</code>" ein eigenständiger Typ ist, deshalb darf für einen Referenzparameter kein weiterer Typ angegeben werden, "<code>int &x</code>" wäre also ungültig!</text>
- </part>
- <author>PeterW</author><date>Juli 2002</date>
- <author>matthes</author><date>Juni 2004</date>
- </doc>