Register for your free account! | Forgot your password?

Go Back   elitepvpers > Other Online Games > Diablo 2 > Diablo 2 Programming
You last visited: Today at 17:49

  • Please register to post and access all features, it's quick, easy and FREE!

Advertisement



[Guide] Grundlagen der D2NT/JavaScript Programmierung

Discussion on [Guide] Grundlagen der D2NT/JavaScript Programmierung within the Diablo 2 Programming forum part of the Diablo 2 category.

Reply
 
Old   #1
Administrator
 
Muddy Waters's Avatar
 
elite*gold: 41624
Join Date: Jan 2010
Posts: 22,728
Received Thanks: 12,654
[Guide] Grundlagen der D2NT/JavaScript Programmierung

Hallo zusammen!

Wie angedroht wird hier mein kleiner Guide entstehen, in dem es um die die Grundlagen der Programmierung von Scripten für D2NT entstehen.
Da ich generell momentan wenig Zeit habe, werde ich nach und nach editieren, je nachdem wie ich Zeit und Lust habe.

Die Struktur ist noch alles andere als Fest und wird eventuell noch drastisch geändert.

Grundsätzliches


Da ich immer wieder nach sehr grundlegenden Zusammenhängen gefragt werde, fand ich ich mal angebracht, diese einfach mal allgemein zu erläutern, um einigen zukünftigen Fragen vorzubeugen.
Der folgende Guide beschäftigt sich mit den Grundlagen der Programmierung in und um D2NT und richtet sich somit in erster Linie an interessierte Einsteiger.

Dabei werde auf niedrigem Niveau starten (Plattdeutsch: from scratch), wobei ich trotzdem versuche, beim Schwerpunkt D2NT zu bleiben und nicht zu sehr in die Theorie abzudriften, auch wenn man um ein paar Dinge einfach nicht herum kommt.
Falls hier in der Einleitung Begriffe auftauchen, die unbekannt sind:
Nicht verzweifeln, ich werde später darauf eingehen.

Ich sollte an dieser Stelle vielleicht anmerken, dass ich kein Informatiker bin, sondern lediglich das Vergnügen hatte, mich zwei Semester lang mit den Grundlagen der Programmierung in C++ zu beschäftigen.
Die Sprachen sind bezüglich der Syntax verwandt, wobei C++ sehr viel komplexer ist, aber das wollen wir ja hier auch nicht lernen.
Trotzdem kann es deshalb sowohl vorkommen, dass ich sachliche Fehler einbaue, mich vor allem aber von den Begrifflichkeiten her sehr an C++ orientiere, sodass sich Leuten die von Java bzw. JavaScript her kommen eventuell an der ein oder anderen Stelle die Nackenhaare aufstellen. In beiden Fällen nehme ich dazu gerne Hinweise per PM entgegen.

Ich lege ausserdem Wert auf eine halbwegs solide Rechtschreibung, denn nur das lässt sich hinterher auch angenehm lesen. Ich finde es gibt nichts schlimmeres, als wenn man einen Satz auch nach zehnmaligem lesen nicht versteht, weil sich dieser jeder sprachlichen Norm entzieht.
Da ich aber zu allem Überfluss auch nur angehender Ingeneur bin, habe ich es selber nicht so mit korrekter Rechtschreibung.
Darum bitte ich auch um Hinweise auf Rechtschreibfehler meinerseits, sowie ungünstig formulierte oder generell unverständliche Sätze, wobei mir hier auch eine PM mit einem Hinweis wünsche, damit das Topic sauber bleibt.

Vorraussetzungen für Interessierte:

  • oder irgend ein vergleichbarer Editor, der Syntaxhighlighting für JavaScript unterstützt
  • Interesse
  • Ein wacher Verstand
Ich werde in meinen Beispielen das Syntaxhighlighting von Notepad++ für JavaScript mit BBCode reproduzieren, sodass die Beispiele bei euch im Editor genauso aussehen, wie hier.

Hinweis zu Notepad++:
Um in Notepad++ das Syntaxhighlighting für JavaScript, für die D2NT typischen Endungen, zu aktivieren, müsst ihr folgendes tun:
  1. Notepad++ starten
  2. Einstellungen --> Stile
  3. Links unter "Sprache" JavaScript auswählen
  4. In der zweiten Box von links unten im Feld "Benutzer-Erw." folgendes einfügen: ntl ntj nip (jeweils durch ein Leerzeichen getrennt)
  5. "Speichern" Button
Nun wird für alle neu geöffneten Dateien des Typs .ntl, .ntj und .nip automatisch das Syntaxhighlighting für JavaScript aktiviert und alles wird sehr viel übersichtlicher.

Weiterführende Links:
Hier ein bisschen Futter für diejenigen, die nicht genug bekommen können oder denen meine Kurzbeschreibungen nicht ausreichen.


Eine sehr viel umfangreichere, englischsprachige Einführung in JavaScript, dessen Lektüre ich wärmstens empfehlen kann.


Eine tolle Referenz für JavaScript Standardfunktionenm, sowie eine recht ausführliche Beschreibung der Grundlagen.


D2NT und D2BS sind sich in vielen Punkten ähnlich, darum kann die D2BS in einigen Punkten Aufschluss über die Umsetzung in D2NT geben.
Aber immer bedenken, dass nicht alles was man dort findet, auch in D2NT so umgesetzt ist.

1. Der Einstieg

Wie viele sicherlich schon bemerkt haben, setzt sich D2NT aus einer vielzahl verschiedener Scripte zusammen. Diese sind nichts anderes als Textdateien, die Quellcode enthalten.
Dieser Quellcode ist für Einsteiger auf den ersten Blick erstmal ein ziemlicher Schlag und will nicht so richtig Sinn ergeben.

Aber keine Angst: Das kann man lernen und den Einstieg dazu versuche ich hier mit euch zu machen.

Aber was ist eigentlich Quelltext?

Als Quelltext, gerne auch Quellcode (Plattdeutsch: Sourcecode), bezeichnet man Dokumente, die Programme oder Programmteile in uncompilierter Form in irgendeiner Art von Programmiersprache enthalten.
Darin ist festgelegt, wie ein Programm abläuft oder einfacher gesagt was ein Programm macht.

Damit ein Programm nun aber auch tatsächlich laufen kann, braucht es natürlich einen Rechner, auf dem das Programm ausgeführt wird.
Nun liegt aber der Quellcode in einer Programmiersprache, beispielsweise JavaScript vor; dummerweise spricht der Rechner aber kein Javascript.
Deshalb muss jeder Quellcode zunächst in eine Sprache übersetzt werden, die der Rechner versteht.

Und das ist Binärcode, also Einsen und Nullen. Um also aus dem Quellcode ein ausführbares Programm zu machen, muss ein Übersetzter her, der z.B. JavaScript in Binärcode übersetzen kann.
Dieses Übersetzen bezeichnet man als compilieren, das Programm was die Übersetzung übernimmt ist dementsprechend der Compiler.

Wie das genau funktioniert soll uns nicht weiter interessieren, es reicht wenn wir wissen, dass unsere D2NT API, also unsere Laufzeitumgebung, alle unsere Scripte zunächst in Binärcode umwandelt und sie dann ausführt.

Was ist eine Programmiersprache?

Nun ist der Begriff ja vorher schon sehr häufig gefallen, aber so richtig erklärt wurde die bedeutung nicht - Zeit dies zu ändern.
Die Sprache, in der alle D2NT Scripte verfasst sind, heisst JavaScript.
Soweit nichts neues, habe ich schließlich schon mehrfach erwähnt, aber was ist denn nun JavaScript?

Eine Programmiersprache im allgemeinen ist eine künstlich geschaffene Sprache, die es erlaubt, (komplexe) logische Zusammenhänge möglichst kompakt und für einen Compiler verarbeitbar - also gut in Binärcode übersetzbar - auszudrücken.

Im Prinzip ist das sehr vergleichbar mit der Sprache die wir sprechen, sei es nun Deutsch oder Englisch. Es gibt bestimmte Wörter, die alle eigene Bedeutungen haben, sowie gewisse Regeln, wie man verschiedene Wörter verknüpft.

Trotzdem gibt es einen entscheidenden Unterschied:
Anders als bei gesprochenen Sprachen, bei denen die Formulierung nicht immer ganz der Norm entsprechen muss (Anm.: Wobei einige hier diese Freiheit wirklich bis zum Exzess bzw. bis zur Unlesbarkeit ausnutzen! ), gibt es in einer Programmiersprache nur richtig oder falsch.
Der Schlüssebegriff dazu ist Syntax.
Die Syntax gibt vor, wie etwas ausgedrückt zu sein hat. Und den letzten Teil sollte man sich zu Herzen nehmen.
Es reicht beim Programmieren nicht, Dinge so zu schreiben, dass sie ungefähr der Syntax entsprechen, es muss alles in allem syntaktisch korrekt sein, ohne Ausnahme.

Ist dies nicht der Fall, meldet sich auch direkt der Compiler zu Wort, wenn wir versuchen, ein Programm mit Syntaxfehlern auszuführen, weil unser Programm vor dem Compilieren immer erst auf syntaktische Fehler untersucht wird.
Das Compilieren wird dann abgebrochen und wir bekommen eine Fehlermeldung. Aus genau diesem Grund sind Syntaxfehler auch nicht so extrem schlimm, weil man den Fehler immer direkt sehen kann, bevor das Programm überhaupt ausgeführt wird.
Schlimmer sind da Logik- bzw- Denkfehler, welche auftreten, wenn ein Programm zwar syntaktisch richtig ist, aber Inhaltlich falsch ist- auf ein paar typische Denkfehler werden wir später noch eingehen.

Syntaxfehler kennt jeder, der schonmal versucht hat, Änderungen an einem Script vorzunehmen, ohne auch nur die leiseste Ahnung zu haben, was er da eigentlich tut - aber das ändern wir jetzt schließlich.

Sogesehen ist der Einstieg in JavaScript vielleicht zunächst vergleichbar mit dem allseits geliebte Vokabel-Lernen aus der Schule. Mit dem Unterschied, dass man mit ein paar wenigen Begriffen schon einiges erreichen kann.

Die "Vokabeln" bestehen hierbei auch eher aus bestimmten Schlüsselbegriffen und der Art und Weise, wie man einige grundsätzlich Dinge umsetzt (Anm.: Gemeint sind Schleifenkonstrukte, Aufbau von Funktionen und Klassen etc.).
Diese Schlüsselbegriffe sind es übrigens auch, die durch das sogenannte Syntaxhighlighting in einem entsprechenden Editor hervorgehoben werden, was ein enormes Plus in Sachen Übersichtlichkeit mit sich bringt und somit absolut zu empfehlen ist.
Sie werden in den folgenden Beispielen auf diese Weise hervorgehoben:
Code:
[COLOR=Navy][B][I]Schlüsselbegriff[/I][/B][/COLOR]
So, genug geplaudert, Zeit mal etwas spezifischer zu werden...

Was ist in Sachen Syntax allgemein zu beachten?

Im folgenden tauchen eventuell einige unbekannte Begriffe auf, darauf wird später genauer eingegangen werden.
Nun zu den Grundregeln der JavaScript Syntax:

Anweisungen
Der wohl häufigst vorkommende Vertreter: Das Semikolon (";"):
Anweisungen enden immer mit einem Semikolon!
Beispiel:
Code:
[COLOR=Navy][B][I]var[/I][/B][/COLOR] a = [COLOR=Red]1[/COLOR];
[COLOR=Navy][B][I]var[/I][/B][/COLOR] b = [COLOR=Red]2[/COLOR];
[COLOR=Green]// Weil Anweisungen immer mit einem Semikolon enden und nicht etwa mit jeder Zeile, kann man theoretisch sein komplettes Programm in eine Zeile schreiben (Was man natürlich nicht macht!)[/COLOR]
[COLOR=Navy][B][I]var[/I][/B][/COLOR] a = [COLOR=Red]1[/COLOR]; [COLOR=Navy][B][I]var[/I][/B][/COLOR] b = [COLOR=Red]2[/COLOR];
Richtiges Einrücken
Jede Blockanweisung wird mit einem (!) Tabulatorsprung eingerückt. Richtiges Einrücken gehört zum guten Stil und erhöht die Übersichtlichkeit enorm. Anweisungsblöcke sind typischerweise durch geschweifte Klammern gekennzeichnet.
Beispiel:
Code:
[COLOR=Green]// Anweisungsblock A[/COLOR]
{
    [COLOR=Green]// Anweisungsblock B[/COLOR]
    {
        [COLOR=Green]// Anweisungsblock C[/COLOR]
        {
        
        }
        
        [COLOR=Green]// Anweisungsblock D[/COLOR]
        {
        
        }
    }

}
Man sieht sehr leicht, dass solche verschachtelten Blöcke ohne richtiges Einrücken schnell unübersichtlich werden, darum immer richtig einrücken.
In Notepad++ funktioniert das übrigens auch wunderbar für mehrere Zeilen, indem diese vorher einfach mit der Maus markiert werden. Bei gedrückter Strg-Taste kann man ausserdem mit der Tabulator Taste Tabulator Sprünge rückgängig machen, also sozusagen nach links einrücken, wobei auch dies mittels Markieren mehrzeilig funktioniert.

Kommentare

Neben ordentlichem Einrücken gehört es auch zum guten Programmierstil, den eigenen Quelltext ordentlich zu kommentieren.
Alles was als Kommentar gekennzeichnet ist, wird vom Compiler ignoriert und somit nicht mitübersetzt.
Und das ganze funktioniert so:
Code:
[COLOR=Green]// Dies ist ein einzeiliger Kommentar

/*
Das ist
ein mehrzeiliger
Kommentar
*/[/COLOR]
Quellcode kann unheimlich komplex werden, darum ist es sehr sinnvoll, seinen Quellcode direkt beim Programmieren zu kommentieren. Das hilft nicht nur anderen, die euren Quellcode nachvollziehen wollen, sondern auch euch selbst.
Denn typischerweise weiss man nach einigen Wochen selber nicht mehr so genau, was man an der ein oder anderen Stelle gemacht hat und dann helfen einem Kommentare weiter, um möglichst schnell wieder Zugang zur Materie zu bekommen.
Kommentare werden durch das Syntaxhighlighting grün hervorgehoben, weshalb ich dies auch für meine Beispiele übernehme, dient es doch dem Auseinanderhalten von Quellcode und Kommentar.

Operatoren
Operatoren sind elementare Schlüsselbegriffe, die allgemein dazu dienen, zwischen zwei Elementen (wie auch immer diese Aussehen mögen) zu vermitteln.
Klingt erstmal sehr abstrakt, ist aber recht leicht verständlich, wenn man einfach mal ein paar Operatoren nennt:
Code:
[COLOR=Green]// Alte Verwandte aus der Mathematik[/COLOR]
+, -, /, *, %
[COLOR=Green]// Und noch ein paar Operatoren[/COLOR]
=, +=, -=, /=, *=, ++, --,  (), [], ?:, ","
[COLOR=Green]// Vergleichsoperatoren[/COLOR]
==, !=, <=, >=
[COLOR=Green]// Logische Operatoren[/COLOR]
&&, ||, !
Auf die Funktionen der verschiedenen Operatoren werde ich später eingehen.

// TODO: Weiteres zum Einstieg?

2. Einfache Variablen

Wir steigen direkt mit einem Überaus wichtigen Thema ein - den Variablen.
Ganz ähnlich wie in der Mathematik sind Variablen in der Programmierung von uns festgelegte "Container", welche Werte - welcher Form auch immer diese einzuordnen sind - repräsentieren können. Ganz analog zur Mathematik haben Variablen Namen bzw. Bezeichner und Werte.

In der Programmierung kommt nun aber ein kleines Detail hinzu:
Weil die Art des Wertes der Variable verschieden sein kann, muss bekannt sein, welche Art von Wert eine Variable haben soll. Und damit kommen wir direkt zum ersten Thema:

Datentypen
Von welcher Art der Wert einer Variablen ist, spiegelt sich in ihrem Datentyp wieder. Denn weil die Variable zur Programmlaufzeit in den Arbeitsspeicher geladen wird, muss bekannt sein, wieviel Speicherplatz diese einnimmt - und genau das verbirgt sich hinter ihrem Datentyp, jeder Datentyp hat benötigt eine spezifische Menge an Speicherplatz.
Auf die einzelnen Größen der verschiedenen Datentypen werde ich hier nicht eingehen, da dies für die D2NT Programmierung praktisch keine Rolle spielt.

Häufig vorkommende Datentypen sind z.B. int(eger), char(acter), bool(ean), double oder string, aber dazu später mehr.

Deklaration und Initialisierung von Variablen
Wer schonmal in einer Sprache wie Java oder C++ programmiert hat, weiss, das eine Variable immer zunächst deklariert, also bekanntgemacht werden muss, bevor man sie verwenden bzw. ihr einen Wert zuweisen kann. In den oben genannten Sprachen schreibt man das dann beispielsweise so:
Code:
[COLOR=Blue][B]int[/B][/COLOR] zahl;
Hierbei wird klar festgelegt, dass die Variable zahl vom Datentyp Integer, also eine Ganzzahl ist.

In JavaScript sieht die Sache etwas anders aus...
Variablen werden zwar genauso deklariert, allerdings taucht statt des Datentyps einfach nur der Schlüsselbegriff var auf. Das soll aber nicht heissen, dass Variablen in Javascript keinen Datentyp haben.
Vielmehr ist es so, dass bei der Zuweisung eines konkreten Wertes gerprüft wird, von welchem Datentyp der zugewiesene Wert ist. Folglich ist die Variable vom Datentyp, des ihr zugewiesenen Wertes, was aber erst bei der Zuweisung entschieden wird.
Somit geträt der Datentyp einer Variable manchmal etwas in den hintergrund, man sollte dennoch wissen, von welchem Datentyp die eigenen Variablen sind.

Soweit so gut, Variablen deklariert man also mit dem Schlüsselwort var, gefolgt von einem Bezeichner. Wie kann man den Variablen aber nun einen konkreten Wert zuweisen, sie also initialisieren?

Die Antwort ist simpel: mit dem Zuweisungsoperator (=)!
Für das obige Beispiel könnte das so aussehen:
Code:
[COLOR=Green]
// Deklaration einer Variablen[/COLOR]
[COLOR=Navy][I][B]var[/B][/I][/COLOR] zahl;

zahl = [COLOR=Red]42[/COLOR];[COLOR=Green] // Der Variablen wird der Wert 42 zugewiesen, es handelt sich somit nun um eine Integer Variable (ganzzahliger Datentyp)[/COLOR]
[COLOR=Green]
// Wir deklarieren eine weitere Variable...[/COLOR]
[COLOR=Navy][I][B]var[/B][/I][/COLOR] nochEineZahl = [COLOR=Red]18[/COLOR];[COLOR=Green] // ...und initialisieren sie im selben Schritt mit dem Wert 18[/COLOR]
[COLOR=Green]
// Das schreit nach einer kleinen Rechnung[/COLOR] :)
[COLOR=Green]// Wir deklarieren noch eine Variable[/COLOR]
[COLOR=Navy][I][B]var[/B][/I][/COLOR] summe;

summe = zahl + nochEineZahl;[COLOR=Green] // Wir weisen dieser Variable die Summe aus zahl und nochEineZahl zu, also 42+18, wobei immer zuerst der zuzuweisende Teil berechnet wird, bevor der alte Wert der Variablen überschrieben wird

// Somit hat die Variable summe nun den Wert 60[/COLOR]
Na das sieht doch ziemlich einfach aus, würde man aber in der Praxis vermutlich etwas kompakter schreiben, z. B. so:
Code:
[COLOR=Navy][I][B]var[/B][/I][/COLOR] zahl = [COLOR=Red]42[/COLOR], nochEineZahl = [COLOR=Red]18[/COLOR], summe = [COLOR=Red]5[/COLOR]; [COLOR=Green]// Deklaration und Initialisierung mehrere Variablen in einer Zeile, durch Kommata separiert, womit man sich zweimal das Schlüsselwort var spart[/COLOR]

[COLOR=Green]// Wie man sieht habe ich der Variablen summe nun auch einen Startwert verpasst, nun addieren wir auf diesen Startwert die Werte der Variablen zahl und nochEineZahl[/COLOR]
summe += zahl + nochEineZahl;[COLOR=Green] // Summe hat nun den Wert 65[/COLOR]
[COLOR=Green]// Der += Operator ist eine Kurzschreibweise: a += b entspricht dabei dem Ausdruck a = a + b[/COLOR]
Um diesen Schritt oben zu verstehen, ist es notwendig zu wissen, dass immer zuerst der zuzuweisende Teil berechnet wird. Die Variable wir erst nach berechnen des Ergebnisses überschrieben. Man muss den Zuweisungsoperator "=" auch wirklich als solchen verstehen und nicht etwa als Gleichheitszeichen im mathematischen Sinne (diesem entspricht im übrigen der Vergleichsoperator, also "==").
Die oben verwendete Kurzschreibweise += gibt es auch noch für die anderen standard Rechenoperationen:
Code:
a += b <=> a = a + b
a -= b <=> a = a - b
a *= b <=> a = a * b
a /= b <=> a = a / b
[COLOR=Green]// Ausserdem:[/COLOR]
a++ <=> a = a + 1
a-- <=> a = a - 1
Nochmal zurück zu den Datentypen, hier einmal die typischen einfachen Datentypen von Variablen, festgelegt durch die jeweils zugewiesenen Werte:
Code:
                              [COLOR=Green] // Datentyp[/COLOR]
[COLOR=Navy][I][B]var[/B][/I][/COLOR]  myInteger = [COLOR=Red]42[/COLOR];           [COLOR=Green]// Integer - Ganzzahl[/COLOR]
[COLOR=Navy][I][B]var[/B][/I][/COLOR]  myDouble = [COLOR=Red]3.14159265[/COLOR];   [COLOR=Green] // Double - Gleitkomma Wert; eigentlich eher Gleitpunkt Wert, denn die Dezimaltrennung erfolgt durch einen Punkt wie in der englischen Schreibweise üblich und nicht wie im Deutschen durch ein Komma[/COLOR]
[COLOR=Navy][I][B]var[/B][/I][/COLOR]  myBool = [COLOR=Navy][B][I]true[/I][/B][/COLOR];            [COLOR=Green]// Boolean - Schaltvariable; Mögliche Werte: true, false[/COLOR]
[COLOR=Navy][I][B]var[/B][/I][/COLOR]  myString = [COLOR=DimGray]"Text"[/COLOR];       [COLOR=Green] // String - Zeichenkette (eigentlich kein einfacher Datentyp);[/COLOR]
Der Datentyp String hat eigentlich nichts bei den einfachen Variablen zu suchen, trotzdem führe ich ihn hier auf, weil die Initialisierung genauso abläuft, wie bei einfachen Variablen.

Implizite und Explizite Typumwandlungen

Die Überschrift klingt direkt mal wieder etwas haarstreubend, was sich dahinter verbirgt ist aber eigentlich nicht weiter schlimm.
Damit gemeint ist eine Konvertierung eines Datentyps in einen anderen, was man gemeinhin als Cast bezeichnet.

Was bedeutet implizit Casten?

Ein Cast ist immer dann implizit, wenn es vom Compiler selbst durchgeführt wird. Implizite Casts programmieren wir also nicht direkt ein, sie finden aber trotzdem statt.
Das geschieht zum Beispiel immer dann, wenn wir mit Zahlen Rechnen, die von unterschiedlichen Datentypen sind, sehr häufig auch im Zusammenhang mit Zeichenketten.

Hier mal ein Beispiel für ein implizites Cast:
Code:
[COLOR=Navy][I][B]var[/B][/I][/COLOR] myInteger = [COLOR=Red]5[/COLOR];  [COLOR=Green]  // Ganzzahl Variable, initialisiert mit dem Wert 5[/COLOR]
[COLOR=Navy][I][B]var [/B][/I][/COLOR]myDouble =[COLOR=Red] 2.5[/COLOR];    [COLOR=Green]// Fließkomma Variable, initialisiert mit dem Wert 2,5[/COLOR]

[COLOR=Green]// Nun Teilen wir die beiden Zahlen durcheinander[/COLOR]
[COLOR=Navy][I][B]var[/B][/I][/COLOR] myResult = myInteger / myDouble; 
[COLOR=Green]// myResult hat nun den Wert 2[/COLOR]
Auf den ersten Blick scheint es sehr eindeutig was dort passiert. Tatsächlich kommt es bei dieser Rechung aber zu einem impliziten Cast, wovon wir so erstmal direkt nichts sehen.
Der Compiler castet nämlich die Integer Variable myInteger nach double, um sie danach durch die double Variable myDouble teilen zu können, womit effektiv 5.0 durch 2.5 geteilt wird.
Wir merken uns also: 5.0 und 5 sind nicht dasselbe, weil unterschiedliche Datentypen dahinter stehen.
Ich hoffe es liegt auf der Hand, warum die Integer Variable in eine Double Variable umgewandelt wird und nicht umgekehrt; Wenn man eine Fließkomma Zahl in eine Ganzzahl umwandelt kommt es zu Datenverlust, die Nachkommastellen gehen dabei verloren.

Implizite Casts finden aber beispielsweise auch immer dann statt, wenn ihr numerische Werte ausgebt. Die Ausgabe Funktion akzeptiert nämlich nur Zeichenketten, darum werden sämtliche numerischen Werte vor der Ausgabe jeweils in eine Zeichenkette umgewandelt, also nach string gecastet.
Auch dies geschieht ganz automatisch, ohne das wir entsprechende Anweisungen einprogrammieren müssten.

Wann ist ein Cast explizit?

Explizite Casts zeichnen sich dadurch aus, dass wir sie ganz bewusst mit einer Anweisung durchführen.
Somit können wir selbst bestimmen, in welcher Form gecastet wird, sodass sich auch Casts ergeben können, die nur bedingt sinnvoll sind.
Zum Beispiel sowas:
Code:
[COLOR=Navy][I][B]var[/B][/I][/COLOR] myDouble = [COLOR=Red]2.5[/COLOR]; [COLOR=Green]   // Fließkomma Variable, initialisiert mit dem Wert 2,5
// Wir wandeln diese Variable nun explizit in eine Ganzzahl um und initialisieren mit dem Ergebnis die Variable myResult:[/COLOR]
[COLOR=Navy][I][B]var[/B][/I][/COLOR] myResult = parseInt(myDouble);
[COLOR=Green]// Welchen Wert hat myResult nun?[/COLOR]
[Spoiler]
[COLOR=Green]/*
Die Antwort lautet: 2
Bei der Umwandlung werden die Nachkommastellen einfach abgeschnitten.
Es wird NICHT gerundet!
*/[/COLOR]
[/Spoiler]
Man kann das ganze aber auch sinnvoll einsetzen.
Zum Beispiel, wenn man numerische Ausdrücke aus Zeichenketten isolieren will, um damit zu rechnen.

Für das Rechnen brauchen wir nämlich numerische Variablen, denn die üblichen Operatoren werden für Zeichenketten nicht funktionieren, weil sie hier andere Funktionen übernehmen (Man sagt auch, sie sind überladen).
Auch dazu ein Beispiel:
Code:
[COLOR=Navy][I][B]var[/B][/I][/COLOR] myString = [COLOR=DimGray]"3.141592654"[/COLOR];   [COLOR=Green] // Initialisieren von myString mit einer konstanten Zeichenkette, die den Wert von Pi repräsentiert
[/COLOR] 
[COLOR=Navy][I][B]var[/B][/I][/COLOR] myDouble = parseFloat(myString); [COLOR=Green]// Initialisieren von myDouble mit dem in eine Gleitkomma Zahl gecasteten Zeichenkette[/COLOR]
[COLOR=Green]// myDouble enthält nun eine Zahl mit der wir nun beliebig weiterrechnen können[/COLOR]
Dies braucht man unter anderem, wenn man aus Daten Dateien ausliest, denn auch dabei ist jede Zeile der Datei für sich eine Zeichenkette.

Solche expliziten Casts funktionieren aber nur, wenn es sich bei dem umzuwandelnden Ausdruck auch um etwas handelt, was sinnvoll Interpretiert werden kann.
So sollte es zum Beispiel nicht gemacht werden:
Code:
[COLOR=Navy][I][B]var[/B][/I][/COLOR] myString = [COLOR=DimGray]"Ich bin garantiert keine Zahl in Zeichenkettenform"[/COLOR];[COLOR=Green]    // Initialisieren von myString mit einer konstanten Zeichenkette[/COLOR]

[COLOR=Navy][I][B]var[/B][/I][/COLOR] myDouble = parseFloat(myString);[COLOR=Green] // Initialisieren von myDouble mit dem in eine Gleitkomma Zahl gecasteten Zeichenkette[/COLOR]
[COLOR=Green]// Welchen Wert hat myDouble nun?[/COLOR]
[Spoiler]
[COLOR=Green]/*
Die Antwort lautet: NaN
NaN steht für "Not a Number" und signalisiert uns, dass aus offensichtlichen Gründen nicht korrekt gecastet werden konnte
*/[/COLOR]
[/Spoiler]
Darum ist bei expliziten Casts auch immer sehr drauf zu achten, was dort eigentlich gecastet wird und wie.

Gültigkeit und Lebensdauer von Variablen
Womit wir mal wieder bei einem eventuell nicht ganz einfachen aber überaus wichtigen Thema sind.

Zentral geht es um die Frage, wo eine Variable gültig ist, also in welchem Bereich unseres Programmes sie definiert ist, sodass wir auch tatsächlich sie zugreifen können.

Nun, ich glaube man kann die Sache relativ kurz auf den Punkt bringen:
Eine Variable ist nach ihrer deklaration immer in dem Block, in dem sie deklariert wurde, sowie sämtlichen dazu untergeordneten Blöcken gültig.

Um das zu demonstrieren, bastle ich einfach mal ein paar Anweisungsblöcke zusammen und deklariere Variablen:
Code:
{ [COLOR=Green]// Anweisungsblock A[/COLOR]
    [COLOR=Navy][I][B]var[/B][/I][/COLOR] a = [COLOR=Red]10[/COLOR];
    { [COLOR=Green]// Anweisungsblock B[/COLOR]
        [COLOR=Navy][I][B]var[/B][/I][/COLOR] b = [COLOR=Red]20[/COLOR];
        
       [COLOR=Green] // <-- Punkt 1[/COLOR]
        {[COLOR=Green] // Anweisungsblock C[/COLOR]
            [COLOR=Navy][I][B]var[/B][/I][/COLOR] c = [COLOR=Red]30[/COLOR];
        }
        
        {[COLOR=Green] // Anweisungsblock D[/COLOR]
            [COLOR=Navy][I][B]var[/B][/I][/COLOR] d = [COLOR=Red]30[/COLOR];
            { [COLOR=Green]// Anweisungsblock E[/COLOR]
                [COLOR=Navy][I][B]var[/B][/I][/COLOR] e = [COLOR=Red]40[/COLOR];
            }
          [COLOR=Green]  // <- Punkt 2[/COLOR]
        }
    
    }
}
[COLOR=Green]// <-- Punkt 3[/COLOR]

[COLOR=Green]// Mal wieder eine kleine Denkaufgabe:
// Ich habe einige Punkte markiert; überlegt euch, welche Variablen bei jedem der Punkte definiert sind.[/COLOR]
[Spoiler]
[COLOR=Green]/*
Punkt 1: a, b
Punkt 2: a, b, d
Punkt 3: keine
*/[/COLOR]
[/Spoiler]
Ich gebe zu, dass man dafür erstmal ein gewisses Gefühl entwickeln muss. Dafür muss man dann aber über solche Details nicht mehr nachdenken, wenn man es sich einmal richtig klar gemacht hat.

Der Überschrift zuliebe muss ich jetzt aber nochmal auf den Begriff Lebensdauer zu sprechen kommen.
Wobei sich das relativ schnell abhandeln lässt, weil JavaScript so etwas wie statische Variablen meines Wissens nicht unterstützt. Was praktisch nichts anderes bedeutet, als dass alle Variablen immer nur in dem Block existieren, in dem sie deklariert wurden.
Wurde dieser Block durchlaufen, ist die Variable und somit auch ihr Wert nicht mehr existent.

An dieser Stelle sollten wir auch nochmal auf die sogenannten globalen Variablen zu sprechen kommen.
Diese erkennt man immer daran, dass sie ausserhalb aller Funktionen deklariert werden, also in keinem Anweisungsblock stehen, sondern typischerweise ganz zu Anfang eines Scripts.
Globale Variablen existieren mit dem Einbinden des Scripts über die gesamte Programmlaufzeit und alle Funktionen des Scripts, in dem sie definiert sind, können auf sie zugreifen.

Bei D2NT sind das meistens Konstanten, also Variablen, die mit einem festen Wert initialisiert werden, welcher danach auch nicht mehr geändert wird.

Bezeichnung von Variablen

Zwar kann man die Bezeichner seiner Variablen frei wählen, solange nur gewisse Schlüsselbegriffe nicht darin vorkommen, trotzdem gibt es auch dabei eine gewisse Norm.

Bei D2NT sind folgende Dinge bei der Bezeichnung von Variablen typisch:
Code:
[COLOR=Green]// Insgesamt gilt, wenn auch nicht D2NT typisch: Jeder Teilbegriff innerhalb eines Bezeichners beginnt mit einem Großbuchstaben, um die Lesbarkeit zu erhöhen; z.B. myAwesomeValue[/COLOR]
[COLOR=Green]// (Globale) Konstante
[/COLOR][COLOR=Navy][I][B]const [/B][/I][/COLOR][COLOR=Green][COLOR=Black]MY_GLOBAL_CONST = [COLOR=Red]42[/COLOR];[/COLOR] // Die Bezeichner von Konstanten bestehen typischerweise nur aus Großbuchstaben
// Globale Variable[/COLOR]
[COLOR=Navy][I][B]var[/B][/I][/COLOR] MyGlobal = [COLOR=Red]2.5[/COLOR]; [COLOR=Green]// Die Bezeichner globaler Variablen beginnen typischerweise mit einem Großbuchstaben[/COLOR]
[COLOR=Green]// Variable innerhalb einer Funktion[/COLOR]
[COLOR=Navy][I][B]var[/B][/I][/COLOR] _myVar = [COLOR=Red]100[/COLOR]; [COLOR=Green]// Die Bezeichner von Variablen innerhalb von Funktionen beginnen mit einem Bodenstrich gefolgt von einem Kleinbuchstaben
// Und als kleiner Ausblick: Formale Parameter von Funktionen; Beginnen mit Kleinbuchstaben z.B. myValue[/COLOR]
Sich daran zu halten bietet große Vorteile, weil man so schon aufgrund des Namens einer Variable weiss, mit was für einer Art von Variablen man es zu tun hat.

Case sensivity
Nun fange ich schon mit Plattdeutschen Überschriften an...
Aber auch dieser Punkt ist enorm wichtig: JavaScript ist in allen belangen case sensitive! Das bedeutet nichts anderes, als dass strikt zwischen Groß- und Kleinschreibung unterschieden wird.
Ihr müsst also bei den Bezeichnern eurer Variablen, wie auch bei sämtlichen Schlüsselbegriffen immer auf die richtige Schreibweise achten.
Schreibt ihr beispielsweise nur einen einzigen Buchstaben im Namen einer eurer zuvor deklarierten Variablen groß, obwohl der Buchstabe eigentlich klein sein sollte, dann sprecht ihr damit alles an, nur nicht eure Variable, auf die ihr eigentlich zugreifen wollt.
Bei den Schlüsselbegriffen sieht es genauso aus, ein falscher Buchstabe führt dazu, dass ein Begriff nicht bekannt ist und somit zu einem Syntaxfehler führt.

3. Arrays

Ein Array ist ein Datenfeld, sozusagen ein Verbund aus, der aus sehr vielen verschiedenen Variablen besteht.
Man könnte sich das bildlich als eine Art Kette von Variablen vorstellen, die natürlich aus vielen verschiedenen Kettengliedern besteht.
Jedes Kettenglied darin hat eine feste Nummer, welche man als Index bezeichnet und welche nullbasiert ist, was nichts anderes bedeutet, als dass das erste Element im Array den Index 0 hat (Unbedingt merken!).
Wie auch schon die normalen Variablen hat ein Array immer einen Bezeichner, wobei die einzelne Elemente mithilfe des Index angesprochen werden können. Der Index wird dazu in eckigen Klammern direkt hinter dem Bezeichner angegeben.
Auch hat jedes Array eine feste Größe, wobei diese dynamische anwachsen kann. Somit ist es kein Problem, mit fester größe erstellte Array nachträglich zu vergrößern, um weitere Daten einzufügen.

Anders als in anderen Programmiersprachen kann ein Array in JavaScript auch Variablen von unterschiedlichen Datentypen enthalten, für D2NT wird man dies aber vermutlich nicht brauchen.
Im übrigen sind Arrays in JavaScript Objekte, wir kommen später dazu, was das genau bedeutet, darum könnten hier einige Dinge etwas aus dem Himmel fallen, ich hoffe diese leuchten dann im späteren Verlauf ein.

Anlegen von Arrays
Hier gibt es verschiedene Möglichkeiten, ich stelle hier nur eine vor, welche aber auch recht häufig verwendet wird:
Code:
[COLOR=Green]// Deklaration eines Arrays der Größe 5 mithilfe des new Operators
[/COLOR][B][COLOR=Navy][I]var[/I][/COLOR][/B][COLOR=Green][COLOR=Black] myArray = [/COLOR][/COLOR][COLOR=Navy][B][I]new[/I][/B][/COLOR][COLOR=Green][COLOR=Black] Array([COLOR=Red]5[/COLOR]);[/COLOR]
// Nun haben wir zwar ein Array der Größe 5, aber alle 5 Felder haben noch keinen Wert
// Darum weisen wir nun jedem Feld einen Wert zu, hier in Form von Zeichenketten; die einzelnen Felder sprechen wir dabei mit dem jeweiligen Feldindex über den [] Operator an:[/COLOR]
myArray[[COLOR=Red]0[/COLOR]] = [COLOR=DimGray]"Das erste Array Element"[/COLOR];
myArray[[COLOR=Red]1[/COLOR]] = [COLOR=DimGray]"Das zweite Array Element"[/COLOR];
myArray[[COLOR=Red]2[/COLOR]] = [COLOR=DimGray]"Das dritte Array Element"[/COLOR];
myArray[[COLOR=Red]3[/COLOR]] = [COLOR=DimGray]"Das vierte Array Element"[/COLOR];
myArray[[COLOR=Red]4[/COLOR]] = [COLOR=DimGray]"Das fünfte Array Element"[/COLOR];
[COLOR=Green]// Das Array enthält nun all diese Zeichenketten und wir genau wie oben bei der Zuweisung mit Index und [] Operator auf den Inhalt der einzelnen Elemente zugreifen[/COLOR]
[COLOR=Green]// Nun ist uns spontan eingefallen, dass wir noch ein Element mehr brauchen... [/COLOR]
[COLOR=Green]// Kein Problem, wir können über die Elementfunktion bzw. Methode push() ganz einfach weitere Elemente hinten an das Array anhängen:[/COLOR]
myArray.push([COLOR=DimGray]"Ein zuvor vergessener Datensatz"[/COLOR]);
[COLOR=Green]// Das Array wurde nun um ein 6. Element erweitert, somit beträgt die Größe nun 6 und wir können wie gehabtmit Index und [] Operator auf den Inhalt dieses neuen Elements zugreifen
// Anders als in anderen Programmiersprachen, kann man aber auch auf Elemente zugreifen und diese Initialisieren, deren Index die aktuelle Größe des Arrays übersteigt
[/COLOR] [COLOR=Green]// Das Array wird dann automatisch bis zu diesem Index erweitert, wobei auch die Größe des Arrays auf den Wert des gewählten Index+1 mitwächst und alle dazwischenliegenden Elemente nicht initialisiert werden, das sieht dann so aus:[/COLOR]
myArray[[COLOR=Red]8[/COLOR]] = [COLOR=DimGray]"Das neunte Array Element"[/COLOR];
[COLOR=Green]// Mit diesem Schritt wird die Größe des Arrays automatisch auf 9 erhöht, wobei nun das das 7. und 8. Element nicht definiert sind[/COLOR]
Durch den Einsatz des [] Operators lassen sich Arrays meist sehr schnell erkennen.
Bei D2NT werden Arrays meist in Schleifen durchlaufen, was den Quellcode sehr kompakt und elegant macht.
Zu Schleifen kommen wir später.

Da es bei D2NT doch öfter mal auftaucht, möchte ich auch noch erwähnen, dass Arrays kombiniert werden können, sodass mehrdimensionale Matrizen entstehen können.
Prinzipiell können dabei Strukturen mit Millionen von Dimensionen entstehen, obwohl in der Regel die Vorstellungskraft schon bei 3 Dimensionen aufhört. Ist aber nicht weiter schlimm, da das in der Praxis und bei D2NT bei 2 bis 3 Dimensionen bleibt.
Hier ein ganz kurzes Beispiel, nur damit ihr was damit anfangen könnt, wenn es euch mal über den Weg läuft:
Code:
[COLOR=Green]// Mal zur Abwechslung ein Beispiel, was sich die meisten vorstellen können: Wir speichern auf ein paar Positionen im D2 Inventar Zahlen:
// Dazu müssen wir zunächst mal einen Ursprung definieren, damit es für jeden nachvollziehbar ist; dieser sei in der oberen linken Inventarecke, womit die oberste Zeile die erste Zeile und die linke Spalte die erste Spalte ist
// Nun brauchen wir zunächst ein Array für die 4 Zeilen, in welche dann später je 10 Inventarfeld kommen:
[/COLOR][B][COLOR=Navy][I]var[/I][/COLOR][/B][COLOR=Green][COLOR=Black] myInventory = [/COLOR][/COLOR][COLOR=Navy][B][I]new[/I][/B][/COLOR][COLOR=Green][COLOR=Black] Array([COLOR=Red]4[/COLOR]);[/COLOR]
// Nun legen wir an jedem Index ein weiteres Array mit 10 Elementen für unsere Inventarfeld an:[/COLOR]
myInventory[[COLOR=Red]0[/COLOR]] = [COLOR=Navy][B][I]new[/I][/B][/COLOR] Array([COLOR=Red]10[/COLOR]); [COLOR=Green]// Zeile 1 (obere Zeile)[/COLOR]
myInventory[[COLOR=Red]1[/COLOR]] = [COLOR=Navy][B][I]new[/I][/B][/COLOR] Array([COLOR=Red]10[/COLOR]); [COLOR=Green]// Zeile 2[/COLOR]
myInventory[[COLOR=Red]2[/COLOR]] = [COLOR=Navy][B][I]new[/I][/B][/COLOR] Array([COLOR=Red]10[/COLOR]); [COLOR=Green]// Zeile 3[/COLOR]
myInventory[[COLOR=Red]3[/COLOR]] = [COLOR=Navy][B][I]new[/I][/B][/COLOR] Array([COLOR=Red]10[/COLOR]); [COLOR=Green]// Zeile 4 (untere Zeile)[/COLOR]
[COLOR=Green]// Was wir nun geschaffen haben ist nichts anderes, als eine Matrix, die der Größe nach ein Inventar beschreiben kann
// Typisch für eine Matrix, werden die einzelnen Felder nun durch Angabe von Zeilen- und Spaltenindex beschrieben; beide zusammen beschreiben ein ganz bestimmtes Inventarfeld
// Ich initialisiere nun ein paar der Inventarfelder:[/COLOR]
myInventory[[COLOR=Red]3[/COLOR]][[COLOR=Red]0[/COLOR]] = [COLOR=Red]42[/COLOR];
myInventory[[COLOR=Red]3[/COLOR]][[COLOR=Red]9[/COLOR]] = [COLOR=Red]12[/COLOR];
myInventory[[COLOR=Red]1[/COLOR]][[COLOR=Red]1[/COLOR]] = [COLOR=Red]9[/COLOR];
[COLOR=Green]// Mal eine Frage für alle die vorher aufgepasst haben: Welchen Wert hat myInventory[3][9] und myInventory[2][8]?[/COLOR]
[SPOILER]
[COLOR=Green]/*
myInventory[3][9]: 12 <-- Dieses Feld haben wir selber initialisiert
myInventory[2][8]: undefined <-- Dieses Feld wurde nicht von uns initialisiert, somit wurde auch noch kein Wert definiert
*/[/COLOR]
[/SPOILER]
Übrigens "weiss" ein Array immer, wie groß es gerade ist. Dies lässt sich über die Klassenvariable length abrufen und sieht dann so aus:
Code:
[B][COLOR=Navy][I]var[/I][/COLOR][/B] myArray = [COLOR=Navy][B][I]new[/I][/B][/COLOR] Array([COLOR=Red]5[/COLOR]);[COLOR=Green] // Deklarieren eines Arrays der Größe 5[/COLOR]
[COLOR=Green]// Wir hängen hinten noch ein weiteres Element an[/COLOR]
myArray.push([COLOR=DimGray]"Ein neues Element"[/COLOR]);
[COLOR=Green]// Nun rufen wir die Größe des Arrays ab und speichern sie in einer weiteren Variable[/COLOR]
[B][COLOR=Navy][I]var[/I][/COLOR][/B] myArraySize = myArray.length;
[COLOR=Green]// Welchen Wert hat myArraySize nun?[/COLOR]
[SPOILER]
[COLOR=Green]/*
Die Antwort lautet: 6
Durch das Anhängen des weiteren Elements ist die Größe von anfänglich 5 auf 6 gewachsen
*/[/COLOR]
[/SPOILER]
Das Abrufen dieser Eigenschaft über den Punktoperator hat damit zu tun, dass ein Array wie schon angedeutet ein Objekt ist, ich werde später beschreiben, was das heisst.
Muddy Waters is offline  
Thanks
42 Users
Old 06/29/2010, 20:36   #2
Administrator
 
Muddy Waters's Avatar
 
elite*gold: 41624
Join Date: Jan 2010
Posts: 22,728
Received Thanks: 12,654
3. Bedingte Anweisungen

So langsam kommen wir zu den interessanten Dingen und hier direkt zu etwas, was man wirklich enorm oft braucht.
Für alle die es bis jetzt noch nicht wussten: Auch wenn Programme auf verschieden Situationen unterschiedlich reagieren können sie trotzdem in keiner Weise selber denken!

Alles was ein Programm also macht, tut es nur, weil sich irgendwann mal ein kreativer Mensch hingesetzt hat und exakt programmiert hat, was das Programm in einer bestimmten Situation zu tun hat.
Genau das geschieht mit Bedingten Anweisungen, also Anweisungen, die nur dann ausgeführt werden, wenn bestimmte vorher festgelegte Bedingungen zutreffen oder auch nicht zutreffen.
Bevor es zum eigentlichen Thema geht, gibt es aber einen kleinen Einschub, der als Grundlage für das Weitere dient.

Logische Ausdrücke und Verknüpfungen
Für alle logischen Ausdrücke gilt: sie können entweder zutreffen (wahr sein) oder nicht zutreffen (falsch sein). Ausdrücke wie "fast richtig" oder "nur teilweise falsch" gibt es nicht!

Darüber hinaus kann man aber auch logische Ausdrücke miteinander Verknüpfen, was dann zu soetwas wie einer "Gesamtaussage" führt, welche dann wieder wahr oder falsch sein kann.
Die Verknüpfung geschieht über das logische UND und das logische ODER. Nun gut, das schreit geradezu nach ein paar pragmatischen Beispielen:

Grundsituation:
Es schneit seit wochen.

Nun könnte eine Aussage lauten: Es ist kalt UND es schneit.
Es ist kalt - offenbar wahr, schließlich schneit es seit wochen
Es schneit - auch wahr
Insgesamt ist die Aussage somit: wahr

Eine weitere Aussage könnte lauten: Es ist 35°C im Schatten UND schneit.
Es ist 35°C im Schatten - sicherlich falsch
Es schneit - wahr
Damit ist die Aussage insgesamt: falsch

Wir Ändern die Aussage minimal: Es ist 35°C im Schatten ODER schneit.
Nach wie vor gilt für die einzelnen Aussagen:
Es ist 35°C im Schatten - sicherlich falsch
Es schneit - wahr
Nun ist die Aussage insgesamt: wahr

Die Beispiele sind reichlich blöd gewählt, aber es geht darum, zu verstehen, welche Aussage sich ergibt, wenn mehrere einzelne Aussagen die für sich wahr oder falsch sein können verknüpft werden.
zusammengefasst sieht das dann so aus:
Code:
[COLOR=Green]/*
Das logische UND entspricht in JavaScript dem Operator &&
Das logische ODER entspricht in JavaScript dem Operator ||
    
    true && false    -> false
    true && true    -> true
    true && !false     -> true
    true || true    -> true
    true || false    -> true
    false || !false    -> true

Der "!" Operator vor einem logischen Ausdruck kehrt diesen um ("!" bedeutet somit nicht); aus falsch wird wahr, aus wahr wird falsch
*/[/COLOR]
Durch das Setzen von Klammern kann man ausgewählte logische Verknüpfungen zuerst auswerten. Im Prinzip funktioniert das genauso wie man es aus der Mathematik kennt: ein Ausdruck in Klammern wird zuerst berechnet und danach mit dem Ergebnis dieser Rechnung weitergerechnet.
Genauso ist es hier, auch dazu wieder ein paar Beispiele:
Code:
[COLOR=Green]/*
    false && (false || true)    -> false
    false && false || true        -> true
    !(true && false) && true    -> true
    !true && false && true        -> false
*/[/COLOR]
Auch wenn Werte jeglicher Art miteinander verglichen werden, ist das Ergebnis ein logischer Ausdruck, welcher nur wahr oder falsch sein kann.
Für Vergleiche werden die folgenden Vergleichsoperatoren verwendet:
Code:
[COLOR=Green]/*
    ==     Genau gleich (Dieser Operator wird von Anfängern gerne mit dem Zuweisungsoperator ("=") verwechselt, es ist also vorsicht geboten!)
    <=     Kleiner oder gleich
    >=     Krößer oder gleich
    <     Echt kleiner als
    >     Echt größer als
*/[/COLOR]
Ich gebe dazu keine Beispiele, weil ich die Verwendung für intuitiv halte, falls die gewünscht werden bitte ich um Rückmeldung.

Die if-Anweisung
Nachdem wir nun ein bisschen was über logische Ausdrücke wissen, können wir uns mit der if-Anweisung befassen. Die if-Anweisung wird gerne als Schleife bezeichnet, was aber sachlich falsch ist, somit vergessen wir diese Sprachweise direkt wieder.

Eine if-Anweisung ist eine Blockanweisung, bei der der Inhalt des Blocks nur dann ausgeführt wird, wenn ein zu überprüfender logischer Ausdruck wahr ist.
Einer if-Anweisung kann ein else-Zweig folgen (muss aber nicht!), der nur dann ausgeführt wird, wenn der zu überprüfende logische Ausdruck falsch ist.

Verdeutlichen wir das ganze in einem Beispiel. Dabei wollen wir den Betrag einer zufälligen Zahl zwischen -10 und 10 berechnen, wie diese Zufallszahl genau erzeugt wird interessiert uns an dieser Stelle nicht, wir nehmen es einfach hin.
Code:
[SIZE=4][SIZE=2][B][I][COLOR=Navy]var[/COLOR][/I][/B][/SIZE][/SIZE][SIZE=4][SIZE=2] myRandomValue, myAbsoluteValue;[COLOR=Green] // Deklarieren von zwei Variablen
[/COLOR]
myRandomValue = Random(-[COLOR=Red]10[/COLOR], [COLOR=Red]10[/COLOR]); [COLOR=Green]// Initialisieren der Variablen myRandomValue mit einer Zufallszahl zwischen -10 und 10
// Die Betragsfunktion macht aus einer negativen Zahl eine positive Zahl, mit positiven Zahlen passiert hingegen nichts[/COLOR]
[COLOR=Green]// Wir überprüfen also, ob die Zufallszahl negativ ist und kehren dann gegebenenfalls das Vorzeichen um[/COLOR]
[COLOR=Navy][I][B]if[/B][/I][/COLOR](myRandomValue < [COLOR=Red]0[/COLOR])[COLOR=Green] // Der logische Ausdruck steht immer in runden Klammern hinter dem Schlüsselbegriff if[/COLOR]
{
    myAbsoluteValue = myRandomValue * -[COLOR=Red]1[/COLOR];[COLOR=Green] // Zuweisen der Zufallszahl mit umgekehrtem Vorzeichen[/COLOR]
}
[/SIZE][/SIZE][SIZE=4][SIZE=2][COLOR=Navy][I][B]else[/B][/I][/COLOR][/SIZE][/SIZE]
[SIZE=4][SIZE=2]     myAbsoluteValue = myRandomValue; [COLOR=Green]// Zuweisen der Zufallszahl ohne Veränderungen, weil die Zahl schon positiv ist[/COLOR]
[/SIZE][/SIZE]
Wichtig: Wann immer im if- oder else-Block mehr als eine Anweisung (soll heissen Zeile) steht, sind geschweifte Klammern zu setzen, um den auszuführenden Block zu begrenzen. Diese dürfen nur dann weggelassen werden, wenn nur eine einzige Anweisung ausgeführt wird.
Eingerückt wird aber grundsätzlich in beiden Fällen!

Natürlich muss das nicht so einfach aussehen, wie oben dargestellt. Oftmals kommen if-Bedingungen verschachtelt vor, sodass man nur hoffen kann, dass derjenige, von dem die Funktion oder das Script stammt, richtig einrücken konnte, denn ansonsten fällt es extrem schwer soetwas nachzuvollziehen.
Also gewöhnt es euch direkt richtig an.

Ich werde das obige Beispiel mal etwas umschreiben, ohne praktischen Nutzen, nur zur Demonstration, wie so eine Verschachtelung aussehen könnte:
Code:
[SIZE=4][SIZE=2][B][I][COLOR=Navy]var[/COLOR][/I][/B][/SIZE][/SIZE][SIZE=4][SIZE=2] myRandomValue, myAbsoluteValue; [COLOR=Green]// Deklarieren von zwei Variablen
[/COLOR]
myRandomValue = Random(-[COLOR=Red]10[/COLOR], [COLOR=Red]10[/COLOR]); [COLOR=Green]// Initialisieren der Variablen myRandomValue mit einer Zufallszahl zwischen -10 und 10
// Die Betragsfunktion macht aus einer negativen Zahl eine positive Zahl, mit positiven Zahlen passiert hingegen nichts[/COLOR]
[COLOR=Green]// Wir überprüfen also, ob die Zufallszahl negativ ist und kehren dann gegebenenfalls das Vorzeichen um[/COLOR]
[/SIZE][/SIZE][SIZE=4][SIZE=2][COLOR=Navy][I][B]if[/B][/I][/COLOR][/SIZE][/SIZE][SIZE=4][SIZE=2](myRandomValue < [COLOR=Red]0[/COLOR])[COLOR=Green] // Der logische Ausdruck steht immer in runden Klammern hinter dem Schlüsselbegriff if[/COLOR]
{
    [/SIZE][/SIZE][SIZE=4][SIZE=2][COLOR=Navy][I][B]if[/B][/I][/COLOR][/SIZE][/SIZE][SIZE=4][SIZE=2](myRandomValue >= [COLOR=Red][COLOR=Black]-[/COLOR]2[/COLOR]) [COLOR=Green]// Wenn myRandomValue zwischen -2 und -1 liegt[/COLOR]
        myRandomValue *= [COLOR=Red]1E3[/COLOR]; [COLOR=Green]// Multipikation mit dem Faktor 1000[/COLOR]
    [/SIZE][/SIZE][SIZE=4][SIZE=2][COLOR=Navy][I][B]else[/B][/I][/COLOR][/SIZE][/SIZE][SIZE=4][SIZE=2][COLOR=Navy][I][B]if[/B][/I][/COLOR][/SIZE][/SIZE][SIZE=4][SIZE=2](myRandomValue >= -[COLOR=Red]6[/COLOR])  [COLOR=Green]// Wenn myRandomValue zwischen -6 und -3 liegt[/COLOR]
        myRandomValue *= [COLOR=Red]1E2[/COLOR]; [COLOR=Green]// Multipikation mit dem Faktor 100[/COLOR]
    [/SIZE][/SIZE][SIZE=4][SIZE=2][COLOR=Navy][I][B]else[/B][/I][/COLOR][/SIZE][/SIZE][SIZE=4][SIZE=2]  [COLOR=Green]// Wenn myRandomValue zwischen -10 und -7 liegt[/COLOR]
        myRandomValue *= [COLOR=Red]10[/COLOR];[COLOR=Green] // Multipikation mit dem Faktor 10[/COLOR]
    
    myAbsoluteValue = myRandomValue * -[COLOR=Red]1[/COLOR]; [COLOR=Green]// Zuweisen der Zufallszahl mit umgekehrtem Vorzeichen[/COLOR]
}
[/SIZE][/SIZE][SIZE=4][SIZE=2][COLOR=Navy][I][B]else[/B][/I][/COLOR][/SIZE][/SIZE]
[SIZE=4][SIZE=2] {
    [/SIZE][/SIZE][SIZE=4][SIZE=2][COLOR=Navy][I][B]if[/B][/I][/COLOR][/SIZE][/SIZE][SIZE=4][SIZE=2](myRandomValue <= [COLOR=Red]2[/COLOR])[COLOR=Green] // Wenn myRandomValue zwischen 0 und 2 liegt[/COLOR]
        myRandomValue *= [COLOR=Red]1E3[/COLOR]; [COLOR=Green]// Multipikation mit dem Faktor 1000[/COLOR]
    [/SIZE][/SIZE][SIZE=4][SIZE=2][COLOR=Navy][I][B]else[/B][/I][/COLOR][/SIZE][/SIZE][SIZE=4][SIZE=2][COLOR=Navy][I][B]if[/B][/I][/COLOR][/SIZE][/SIZE][SIZE=4][SIZE=2](myRandomValue <= [COLOR=Red]6[/COLOR])  [COLOR=Green]// Wenn myRandomValue zwischen 3 und 6 liegt[/COLOR]
        myRandomValue *= [COLOR=Red]1E2[/COLOR]; [COLOR=Green]// Multipikation mit dem Faktor 100[/COLOR]
    [COLOR=Navy][I][B]else[/B][/I][/COLOR] [COLOR=Green] // Wenn myRandomValue zwischen 7 und 10 liegt[/COLOR]
        myRandomValue *= [COLOR=Red]10[/COLOR]; [COLOR=Green]// Multipikation mit dem Faktor 10[/COLOR]
        
    myAbsoluteValue = myRandomValue;[COLOR=Green] // Zuweisen der Zufallszahl ohne Veränderungen, weil die Zahl schon positiv ist[/COLOR]
}
[/SIZE][/SIZE]
Nicht wirklich sinnvoll, trotzdem sieht man so eine gewisse Verschachtelung.
Zeit fürs nächste Thema...

Die switch-Anweisung
Sie ist verwandt mit der if-Anweisung, was man auch daran erkennt, das man jede switch-Anweisung in ein Konstrukt von if-else if-Blöcken überführen kann. Umgekehrt funktioniert das aber längst nicht immer.
Das liegt daran, dass in einer switch Anweisung nur auf Gleichheit überprüft werden kann. Dazu wird eine Variable mit Konstanten Werten verglichen und bei Übereinstimmung, ein bestimmter Block ausgeführt wird.

Die einzelnen Fälle werden mit dem Schlüsselwort case getrennt, gefolgt von dem zu Vergleichenden Wert und einem Doppelpunkt.
Ich glaube ich lege direkt wieder ein Beispiel vor, damit man die Erklärung besser nachvollziehen kann. Im Beispiel soll es darum gehen den richtigen Trank zu trinken, wobei wir das mit dem Tränke trinken nur als Kommentar andeuten, wie eine Funktion aussieht, die das macht interessiert hier erstmal nicht:
Code:
[COLOR=Green]// Wir definieren 3 IDs; HP entspricht 0, MP entspricht 1, RJUV entspricht 2[/COLOR]
[COLOR=Navy][B][I]var[/I][/B][/COLOR] potionID = Random([COLOR=Red]0[/COLOR], [COLOR=Red]2[/COLOR]); [COLOR=Green]// Für unsere ID initialisieren wir eine Variable mit einer Zufallszahl zwischen 0 und 2
[/COLOR] 
[COLOR=Green]// Nun bauen wir eine switch-Anweisung auf[/COLOR]:
[COLOR=Navy][B][I]switch[/I][/B][/COLOR](potionID) [COLOR=Green]// Der Aufbau sieht ähnlich aus wie bei der if-Anweisung, nur dass in den runden Klammern direkt der Ausdruck steht, der mit den möglichen Fällen verglichen werden soll[/COLOR]
{
    [B][I][COLOR=Navy]case[/COLOR][/I][/B] [COLOR=Red]0[/COLOR]:
        [COLOR=Green]//<-- Aufruf einer Funktion zum trinken eines Heiltranks[/COLOR]
        [B][I][COLOR=Navy]break[/COLOR][/I][/B]; [COLOR=Green]// Dies muss am Ende von JEDEM case stehen[/COLOR]
    [B][I][COLOR=Navy]case[/COLOR][/I][/B] [COLOR=Red]1[/COLOR]:
        [COLOR=Green]//<-- Aufruf einer Funktion zum trinken eines Manatranks[/COLOR]
        [COLOR=Navy][B][I]break[/I][/B][/COLOR];
    [B][I][COLOR=Navy]case[/COLOR][/I][/B] [COLOR=Red]2[/COLOR]:
        [COLOR=Green]//<-- Aufruf einer Funktion zum trinken einer Rejuvenation Potion[/COLOR]
        [B][I][COLOR=Navy]break[/COLOR][/I][/B];
    [I][B][COLOR=Navy]default[/COLOR][/B][/I]: [COLOR=Green]// Optional[/COLOR]
        [COLOR=Green]// Was hier steht, wir immer ausgeführt, wenn keiner der anderen Fälle zutrifft[/COLOR]
}
Wie man sieht müssen bei den einzelnen cases keine geschweiften Klammern stehen. Insgesamt ist wieder darauf zu achten, ordentlich einzurücken, und vor allem das break am Ende eines jeden cases nicht zu vergessen. Dies verhindert, dass der Default Fall ausgeführt wird, obwohl schon einer der anderen Fälle zutreffend war.

4. Schleifen

Im folgenden Abschnitt wollen wir uns mit einem weiteren elementaren Teil der Programmierung auseinandersetzen, den Schleifen (Plattdeutsch: loops).

Hierbei gibt es verschiedene Arten von Schleifen, wobei man ein paar Aussagen treffen kann, die für alle Arten von Schleifen gültig sind.
Eine Schleife hat immer die Möglichkeit, einen Anweisungsblock mehrfach auszuführen, auch wenn es vorkommen kann, dass der Anweisungsblock nicht ein einziges mal ausgeführt. Das ist übrigens auch der entscheidende Unterschied gegenüber if-Anweisungen. Deren Anweiungsblock kann per Prinzip immer nur einmal ausgeführt werden, darum sollte man hierbei klar unterscheiden und diese beiden Konstrukte nicht durcheinanderwürfeln.
Jede Schleife hat soetwas wie eine Durchlaufbedingung, also wieder ein logischer Ausdruck. Solange dieser Ausdruck wahr ist, wird der Anweisungsblock ausgeführt. Darum ist der logische Ausdruck - wie immer dieser auch aussehen mag - in aller Regel dynamisch, sodass der Ausdruck zunächst wahr ist, aber nach einer gewissen Anzahl an Schleifendurchläufen falsch wird, sodass die Schleife verlassen wird.
Ist der Ausdruck allerdings immer wahr, läuft die Schleife unendlich of durch, darum spricht man dann von einer Endlosschleife (Plattdeutsch: infinite loop). Dies passiert oft aufgrund von Denkfehlern beim Programmieren, kann aber auch gewollt sein. Trotzdem sollte man immer einen besonders kritischen Blick beim Programmieren von Schleifen haben, um ungewollte Endlosschleifen zu vermeiden.
Zeit uns die erste Schleife genauer anzuschauen...

Die while-Schleife
Bei der while-Schleife handelt es sich eigentlich um die Schleife schlechthin, alle anderen Schleifen leiten sich davon ab.
Im Bezug auf die Syntax beginnt die while-Schleife mit dem Schlüsselwort while gefolgt von einem logischen Ausdruck in runden Klammern.
Danach wird mit geschweiften Klammern der auszuführende Anweisungsblock begrenzt, welcher dann solange ausgeführt wird, wie der logische Ausdruck wahr ist.
Zeit für ein Beispiel:
Code:
[COLOR=Navy][B][I]var[/I][/B][/COLOR] myLoops = [COLOR=Red]1[/COLOR];[COLOR=Green] // Wir deklarieren eine Variable und initialisieren sie mit 1 - Sie soll später die Schleifendurchläufe Zählen[/COLOR]

[COLOR=Navy][B][I]while[/I][/B][/COLOR](myLoops < [COLOR=Red]10[/COLOR])
{
   [COLOR=Green] // Alles was in diesem Anweisungsblock steht wird solange ausgeführt, wie die Durchlaufbedingung der Schleife wahr ist, also solange myLoops nicht größer als 9 ist[/COLOR]
    myLoops++; [COLOR=Green]// Wir Inkrementieren die Variable Zählvariable in jedem Durchlauf[/COLOR]
    
   [COLOR=Green] // <-- Wir Stellen uns vor, wir würden an dieser Stelle den Wert von myLoops auslesen[/COLOR]
}
[COLOR=Green]// Zeit für ein paar einfache Fragen zum Mitdenken:

// Wie oft läuft die Schleife insgesamt durch?
// Welchen Wert hat myLoops an der markierten Stelle in jedem Durchlauf?[/COLOR]
[SPOILER]
[COLOR=Green]/*[/COLOR]
[COLOR=Green]Die Schleife läuft insgesamt 9 mal durch.
Die Variable nimmt folgende Werte an: 2, 3, 4, 5, 6, 7, 8, 9, 10
*/
[/COLOR] [/SPOILER]
Soweit erstmal sehr einfach, sowas wird erst schwierig, wenn auch die Anweisungsblöcke und die Durchlaufbedingung komplexer werden.
Ich werde das obige Beispiel mal etwas erweitern, indem ich zwei wichtige Schlüsselbegriffe einbringe, die man oft in Schleifen einsetzt, nämlich:
  • break - Verlässt die (nächste übergeordnete) Schleife und setzt das Programm hinter dem Anweisungsblock der Schleife fort
  • continue - Springt zum Schleifenkopf, ohne den nachfolgenden Teil des Anweisungsblocks auszuführen

Mit diesem Wissen schreiten wir direkt zur Tat:
Code:
[COLOR=Navy][B][I]var[/I][/B][/COLOR] myLoops = [COLOR=Red]1[/COLOR]; [COLOR=Green]// Wir deklarieren eine Variable und initialisieren sie mit 1 - Sie soll später die Schleifendurchläufe Zählen[/COLOR]

[B][I][COLOR=Navy]while[/COLOR][/I][/B](myLoops < [COLOR=Red]10[/COLOR])
{
    [COLOR=Navy][B][I]if[/I][/B][/COLOR](myLoops == [COLOR=Red]8[/COLOR]) [COLOR=Green]// Wenn myLoops den Wert 8 hat[/COLOR]
        [B][I][COLOR=Navy]break[/COLOR][/I][/B]; 
    [COLOR=Navy][B][I]if[/I][/B][/COLOR](myLoops % [COLOR=Red]3[/COLOR] == 0)[COLOR=Green] // Wenn myLoops durch 3 teilbar ist, ohne dass ein Rest übrig bleibt (Das Teil nennt man Modulo Operator, ist speziell bei Schleifen praktisch)[/COLOR]
    {
        myLoops += [COLOR=Red]2[/COLOR];
        [COLOR=Navy][B][I]continue[/I][/B][/COLOR];
    }
        
    myLoops++; [COLOR=Green]// Wir Inkrementieren die Variable Zählvariable in jedem Durchlauf[/COLOR]
    
}
[COLOR=Green]// <-- Markierung[/COLOR]

[COLOR=Green]// Nun stelle ich fast dieselben Fragen wie eben:

// Wie oft läuft die Schleife insgesamt durch?
// Welchen Wert hat myLoops an der markierten Stelle?[/COLOR]
[SPOILER]
[COLOR=Green]/*
Die Schleife läuft insgesamt 6 mal durch.
Der Wert beträgt 8.
*/[/COLOR]
[/SPOILER]
Ich hoffe durch die Beispiele ist ein bisschen deutlich geworden, was eine while-Schleife eigentlich tut.
Denn jetzt geht es direkt weiter zu einem engen Verwandten der while-Schleife...

Die do-while-Schleife
Prinzipiell funktioniert die do-while-Schleife genau wie die while Schleife, mit dem Unterschied, dass sie immer mindestens einmal durchläuft. Das liegt daran, dass die Durchlaufbedingung der do-while-Schleife erst nach dem Anweisungsblock überprüft wird.
Das typische Beispiel für do-while-Schleifen sind Passwortabfragen, bei irgendwelchen Programmen. Dabei soll in der Regel solange das Passwort abgefragt werden, bis der Nutzer das korrekte Passwort eingibt. Da nun aber die eigentliche Abfrage der Nutzereingabe im Anweisungsblock der Schleife stattfindet, muss diese immer mindestens einmal durchlaufen, damit der Nutzer überhaupt die Möglichkeit hat, etwas einzugeben.

Auch hierzu ein kurzes Beispiel, nur um die Syntax der do-while-Schleife zu veranschaulichen:
Code:
[B][I][COLOR=Navy]var[/COLOR][/I][/B] myLoops = 100;[COLOR=Green] // Wir starten mal mit einem Wert, der eigentlich die Durchlaufbedingung der Schleife falsch werden lässt[/COLOR]

[COLOR=Navy][B][I]do[/I][/B][/COLOR]
{[COLOR=Green] // Trotzdem wird dieser Teil hier ausgeführt :)[/COLOR]
    [B][I][COLOR=Navy]if[/COLOR][/I][/B](myLoops == [COLOR=Red]100[/COLOR])
        myLoops = [COLOR=Red]1[/COLOR]; [COLOR=Green]// Wir weisen myLoops den Wert 1 zu[/COLOR]
        
    myLoops++;[COLOR=Green] // ...und inkrementieren myLoops in jedem Schleifendurchlauf[/COLOR]

}[B][I][COLOR=Navy]while[/COLOR][/I][/B](myLoops < [COLOR=Red]10[/COLOR])
Diese Schleife macht nun exakt dasselbe, wie die while-Schleife im ersten Beispiel. Man sieht aber hoffentlich den Unterschied; eine while Schleife würde bei dem hier gewählten Startwert nicht ein einziges mal durchlaufen, die do-while-Schleife hingegeb schon, sodass wir einfach im ersten Durchlauf den Wert der Variable auf 1 senken können, sodass die Schleife auch hier mehrfach durchläuft.

Die for-Schleife
Auch die for-Schleife ist eng mit der while Schleife verwandt, was man auch daran sieht, dass man jede for-Schleife genausogut mit einer while-Schleife ausdrücken kann, umgekehrt muss das aber nicht immer funktionieren.
Die for-Schleife ist sogesehen nichts anderes als eine Kurzschreibweise. Sie ist eine Zählschleife und wird immer dann eingesetzt, wenn wir vorher die erforderliche Anzahl an Schleifendurchläufen kennen.

Die Syntax der for-Schleife ist etwas komplizierter, weil hier direkt Deklarierung und Initialisierung einer Zählvariablen, Vergleich der Zählvariablen mit einem Endwert, sowie die Veränderung der Zählvariablen mit jedem Durchlauf in einem einzigen kompakten Ausdruck erfolgen.
Diese kurze Schreibweise bringt aber auch Übersichtlichkeit, da man so alles auf einen Blick vor sich hat.
Auch hierzu gibt es wieder ein Beispiel zur Veranschaulichung:
Code:
[B][I][COLOR=Navy]for[/COLOR][/I][/B](var i = [COLOR=Red]0[/COLOR]; i < [COLOR=Red]10[/COLOR]; i[COLOR=Black]++[/COLOR]) [COLOR=Green]// Wie man sieht haben wir 3 Teile: Variablen (Deklaration und) Initialisierung, Vergleich mit einem Maximalwert, Veränderung der Laufvariablen[/COLOR]
{
    [COLOR=Navy][I][B]if[/B][/I][/COLOR](i == [COLOR=Red]5[/COLOR])
        [COLOR=Navy][B][I]break[/I][/B][/COLOR];
} [COLOR=Green]// An dieser Stelle wird i Verändert bzw. in diesem Fall inkrementiert, danach wird die Durchlaufbedingung geprüft[/COLOR]
Dazu noch ein paar Anmerkungen:
Die Bezeichnung i ist typisch für Zählschleifen. Es werden eigentlich nur andere Bezeichnungen gewählt, wenn die Bezeichnung i im aktuellen Gültigkeitsbereich schon besetzt ist.
Die Laufvariable wird nur beim ersten Durchlauf initialisiert und ist bei direkter Deklarierung in der runden Klammer der for Schleife nur innerhalb des Anweisungsblocks der for-Schleife gültig.
Die Veränderung der Laufvariablen Erfolgt immer nachdem der ganze Anweisungsblock durchlaufen wurde, direkt danach erfolgt die Überprüfung der Durchlaufbedingung.

Nun möchte ich mal demonstrieren, wie man das ganze auch tatsächlich sinnvoll einsetzen kann.
Man möge sich an dieser Stelle an das im Kapitel über Arrays gelernte erinnern, denn genau darauf kommen wir jetzt zurück.
Mit einer for-Schleife können wir beispielsweise sehr leicht den Index eines Datensatzes in einem Array finden. Als Beispiel erstellen wir uns dazu einfach ein Array mit ein paar Namen, wobei wir hinterher den Index eines bestimmten Namens finden wollen.

Los gehts:
Code:
[COLOR=Green]// Zunächst basteln wir uns mal ein Array zurecht und speichern ein paar[/COLOR] [COLOR=Green]Namen darin[/COLOR]
[B][I][COLOR=Navy]var[/COLOR][/I][/B] names = new Array([COLOR=Red]5[/COLOR]);
names[[COLOR=Red]0[/COLOR]] = [COLOR=DimGray]"robert johnson"[/COLOR];
names[[COLOR=Red]1[/COLOR]] = [COLOR=DimGray]"stevie ray vaughan"[/COLOR];
names[[COLOR=Red]2[/COLOR]] = [COLOR=DimGray]"john lee hooker"[/COLOR];
names[[COLOR=Red]3[/COLOR]] = [COLOR=DimGray]"albert collins"[/COLOR];
names[[COLOR=Red]4[/COLOR]] = [COLOR=DimGray]"buddy guy"[/COLOR];
[COLOR=Green]// Nun kennen wir die Indizes zwar - wir gehen jetzt aber mal davon aus, dass wir sie in der Praxis tatsächlich suchen müssten - Und das geht beispielsweise so:[/COLOR]

[COLOR=Navy][I][B]var[/B][/I][/COLOR] i = [COLOR=Red]0[/COLOR]; [COLOR=Green]// Die Zählvariable soll hinterher unseren Index angeben, darum deklarieren wir sie ausserhalb, damit sie auch nach Durchlaufen der Schleife noch existiert[/COLOR]

[B][I][COLOR=Navy]for[/COLOR][/I][/B](; i < names.length; i[COLOR=Black]++[/COLOR]) [COLOR=Green]// Der erste Teil entfällt, weil wir die Zählvariable schon ausserhalb deklariert und initialisiert haben; als Vergleichswert wählen wir die Größe des Arrays, so durchlaufen wir exakt alle Elemente des Arrays[/COLOR]
{
    [COLOR=Navy][B][I]if[/I][/B][/COLOR](names[i] == [COLOR=DimGray]"albert collins"[/COLOR])
        [B][I][COLOR=Navy]break[/COLOR][/I][/B]; [COLOR=Green]// Wir verlassen die Schleife, wenn eine Übereinstimmung gefunden werden konnte, i entspricht nun dem Index des gesuchten Elements[/COLOR]
} 
[COLOR=Green]// Wurde nun ein Datensatz gefunden, der unseren Suchkriterien entspricht, ist i der Index dieses Elements; existiert kein solcher Datensatz, hat i den wert names.length, also in diesem Fall 5[/COLOR]

[COLOR=Navy][B][I]if[/I][/B][/COLOR](i < names.length)[COLOR=Green] // Wenn der gesuchte Datensatz im Array vorhanden ist[/COLOR]
{
   [COLOR=Green] // Damit könnte man nun irgendwas sinnvolles tun... :)[/COLOR]
}
Die Laufvariable i hat bei diesem Beispiel natürlich am Ende den Wert 3, schließlich entspricht das gerade dem Datensatz, nach dem gesucht wurde.

for-Schleifen in Verbindung mit Arrays tauchen bei D2NT sehr häufig auf, darum hilft es, wenn man die Sache einmal richtig verstanden hat.


5. Funktionen

Nun sind wir schon bei einem etwas umfangreicheren und eventuell auch etwas schwierigerem Thema angekommen, also steigen wir direkt voll ein...

Was ist eine Funktion in der Programmierung?
Eine Funktion ist nichts anderes als eine Art Ablaufplan oder Unterprogramm, das wir bei Bedarf aufrufen und das uns typischerweise hinterher einen Rückgabewert zurückgibt (muss es nicht!).
Funktionen werden immer dann eingesetzt, wenn gleiche oder extrem ähnliche Abläufe im Programm mehrfach auftreten. Diese werden dann in Form einer Funktion programmiert, was den Quellcode als ganzes schlanker und übersichtlicher macht.

Auch können wir der Funktion bei ihrem Aufruf gewisse Eigenschaften übergeben, also Werte in Form von einfachen sowie komplexen Variablen, oder auch einfach Konstanten. Diese Werte Bezeichnet man als Parameter.
Wenn wir eine Funktion schreiben, müssen wir die Parameter bennenen und vor allem die Reihenfolge festlegen. Dieses Konstrukt aus übergebenen Datentypen und deren exakter Reihenfolge bezeichnet man als Parametersignatur.
Die Parametersignatur muss beim Aufruf einer Funktion immer bekannt sein und eingehalten werden. Eine Funktion kann auch so definiert sein, dass keine Parameter übergeben werden.

Bevor ich weiter die Theorie in Worte fasse möchte ich aber zunächst wieder ein Beispiel geben, damit das ganze nachvollziehbar wird.

Dazu suchen wir uns mal wieder ein Beispiel, mal wieder was möglichst einfaches: Wir berechnen den Flächeninhalt eines Rechtecks.
Die zu realisierende Funktion soll muss also folgende Eigenschaften haben:
  • Übergabe von zwei Parametern für die Seitenlängen
  • Optional aber sinnvoll: Überprüfen der erhaltenen Parameter (Sind es Zahlen? Sind sie positiv?)
  • Berechnung des Flächeninhaltes mithilfe der übergebenen Parameter
  • Rückgabe des Flächeninhaltes


Wie definiert man eine Funktion?
Was nun folgt ist die Definition der Funktion, wir legen hier also nun fest, wie die Funktion aufgebaut ist und was sie letztendlich macht.
Funktionsdefinitionen beginnen immer mit dem Schlüsselwort function gefolgt vom Namen der Funktion und der Parameterliste (hierbei handelt es sich übrigens um sogenannte Formalparameter).
Das sieht dann z.B. so aus:
Code:
[COLOR=Green]// Ich wähle die Bezeichnungen mal auf Englisch; erstens ist das generell so üblich, zweitens hätte ich mit "höhe" sonst einen Umlaut drin und die haben in Bezeichnern wirklich gar nichts verloren...[/COLOR]
[COLOR=Navy][B][I]function[/I][/B][/COLOR] CalcRectArea(height, width)
{
    [COLOR=Green]// Funktionseigene Variablen[/COLOR]
    [COLOR=Navy][B][I]var[/I][/B][/COLOR] _area;
    [COLOR=Green]// Prüfen der übergebenen Parameter[/COLOR]
    [COLOR=Navy][B][I]if[/I][/B][/COLOR](height < [COLOR=Red]0[/COLOR] || width < [COLOR=Red]0[/COLOR] || isNaN(height) || isNaN(width)) [COLOR=Green]// Wenn einer der übergebenen Parameter kleiner Null ist oder keine Zahl ist[/COLOR]
        [COLOR=Navy][B][I]return[/I][/B][/COLOR] -[COLOR=Red]1[/COLOR]; [COLOR=Green]// Gib den Wert -1 zurück, was den Fehlerfall signalisieren soll[/COLOR]
    //[COLOR=Green] Anzumerken ist, dass die Funktion mit dem retrun endet, sprich alles was jetzt folgt, würde im Fehlerfall gar nicht erst ausgeführt
    // Die Parameter scheinen in Ordnung, Zeit die Fläche zu berechnen:[/COLOR]
    _area = height * width;
  [COLOR=Green]  // _area enthält nun den Flächeninhalt des Rechtecks[/COLOR]
    [COLOR=Navy][B][I]return[/I][/B][/COLOR] _area;[COLOR=Green] // Wir geben den berechneten Flächeninhalt zurück[/COLOR]
}
So könnte nun die Definition einer Funktion aussehen, die das gegebenen Problem löst. Wichtig ist, dass wir uns merken, dass eine Funktion immer mit einem return endet, das heisst etwaiger Quellcode der darauf folgt wird nicht ausgeführt (ein bisschen vergleichbar mit dem break in einer Schleife).
Jetzt stellt sich natürlich die Frage, wie man nun konkret mit der Funktion arbeiten kann...

Wie sieht der Funktionsaufruf aus?
Damit wir die Funktion überhaupt aufrufen können, müssen wir sicherstellen, dass sie an ihrem Einsatzort auch definiert ist. Das ist der Fall, wenn sich die obige Definition irgendwo im selben Script wiederfindet oder in einem anderen Script enthalten ist, welches wir in unser Script eingebunden haben.
Für den folgenden Aufruf gehen wir davon aus, dass unsere Funktion an dieser Stelle definiert ist. Der Aufruf erfolgt ähnlich der Definition mit dem Funktionsnamen und runden Klammern, in denen die durch Kommata getrennten Übergabeparameter stehen. Diese tatsächlich existierenden übergebenen Werte bezeichnet man auch als Aktualparameter.
Ganz wichtig ist, dass bei der Übergabe auf die Parametersignatur der Funktion geachtet wird! Haben wir unsere Funktion mit zwei Parametern height und width definiert, dann muss auch die Reihenfolge der übergebenen Werte gleich sein, also zuerst die Höhe und dann die Breite und nicht wie es einem gerade passt.
(Das mag in diesem Fall egal sein, aber bei komplexeren Funktionen haben die verschiedenen Parameter oft nichts miteinander zu tun und wenn die Funktion dann z.B. eine Zeichenkette statt einer Zahl übergeben bekommt, sind Fehler absehbar.)

Die Rückgabewerte sind übrigens recht kurzlebig. Wann immer wir also einen Rückgabewert länger brauchen, als nur für einen kurzen Vergleich, müssen wir ihn in einer Variable zwischenspeichern, was wir im folgenden Beispiel auch zu Demonstrationszwecksen tun werden.
Zeit unsere Funktion in Aktion zu sehen:
Code:
[COLOR=Navy][B][I]var[/I][/B][/COLOR] myH = [COLOR=Red]8[/COLOR]; [COLOR=Green]// Initialisieren einer Variablen für die Höhe[/COLOR]
[COLOR=Navy][B][I]var[/I][/B][/COLOR] myW = [COLOR=Red]5[/COLOR]; [COLOR=Green]// Initialisieren einer Variablen für die Breite[/COLOR]

[COLOR=Navy][B][I]var[/I][/B][/COLOR] area = CalcRectArea(myH, myW); [COLOR=Green]// Wir Rufen die Funktion mit beiden Parametern auf, der Rückgabewert wird direkt in der Variable area gespeichert[/COLOR]

[COLOR=Green]// Man muss den Rückgabewert nicht speichern, für einen kurzen Vergleich kann man beispielsweise darauf verzichten
// Auch dazu ein Beispiel; man nehme zwei Rechtecke mit Höhe1 = 5, Breite1=5 und Höhe2 = 10, Breite2 = 4
// Nun vergleichen wir die Flächeninhalte der Rechtecke, wobei wir die Seitenlängen als Konstanten übergeben[/COLOR]
[COLOR=Navy][B][I]if[/I][/B][/COLOR](CalcRectArea([COLOR=Red]5[/COLOR], [COLOR=Red]5[/COLOR]) < CalcRectArea([COLOR=Red]10[/COLOR], [COLOR=Red]4[/COLOR]))
{
   [COLOR=Green] // Rechteck 1 ist kleiner als Rechteck 2[/COLOR]
}
[COLOR=Navy][B][I]else[/I][/B][/COLOR]
{
    [COLOR=Green]// Rechteck 2 ist kleiner als Rechteck 1[/COLOR]
}
Was tatsächlich beim Aufruf passiert, ist ein Sprung des Prozesszeigers. Das Programm "merkt" sich also an welcher Stelle die Funktion aufgerufen wurde mitsamt den übergebenen Parameter und springt dann in die eigentliche Funktion.
Wenn die Funktion komplett durchlaufen wurde, oder wenn ein return ausgeführt wird, erfolgt der Sprung zurück zu der Position des Funktionsaufrufes, mitsamt einem etwaigen Rückgabewert der Funktion. Nach dem Aufruf läuft das Script ganz normal weiter.

Ich möchte nun nochmal auf die Parameterstruktur von Funktionen zurückkommen.
Wir hatten ja festgelegt, dass die Aktualparameter beim Funktionsaufruf immer mit der Parametersignatur der Funktion übereinstimmen müssen, was bedeutet, dass die Parameter, die man übergibt, von der Anzahl und von den Datentypen mit den Formalparametern der Funktion übereinstimmen muss.
Auf das obige Beispiel übertragen heisst das, dass ich der Funktion CalcRectArea() nicht einfach 2 Zeichenketten als Parameter übergebe (obwohl die Überprüfung der Parameter in diesem Fall einen Absturz verhindern würde), genausowenig wie ich der Funktion 10 verschiedene Zahlen übergebe, anstatt der erwarteten zwei.

Während mehr übergebene Parameter als in der Parametersignatur festgelegt meist einen Denkfehler als Hintergrund haben, ist es durchaus möglich, eine Fuktion unterzuversorgen, ihr also weniger Parameter zu übergeben, als in der Parametersignatur definiert.
In JavaScript geschieht das für meinen Geschmack etwas durch die Hintertür, in C++ sind solche Defaultparameter sehr viel offensichtlicher. Eins ist dabei aber gleich: Defaultparameter dürfen immer nur die letzten Parameter einer Funktion sein, alle anderen Parameter die grundsätzlich immer übergeben werden, haben deshalb vor den Defaultparametern zu stehen.

Was nützen aber nun solche Defaultparameter?

Wie der Name schon sagt kann man damit gewisse Standardfälle abdecken, sodass man beim Aufruf der Funktion nurnoch weniger oder sogar garkeine Parameter übergeben muss.
Der Großteil der Funktionen in D2NT verfügt über Defaultparameter, sodass wir mit dem nun gesammelten vorwissen ruhig mal auf ein Beispiel aus dem wahren Leben zurückgreifen können.
Als Beispiel dient uns nun ein Auszug aus der Funktion NTM_MoveTo(), welche in der NTMove.ntl definiert ist. Ich habe den interessanten Teil mal hinter einem Spoiler versteckt, schaut euch doch zunächst mal den Teil darunter an, bevor ihr die Funktion selbst seht:
Code:
[COLOR=Navy][B][I]function[/I][/B][/COLOR] NTM_MoveTo(areaid, x, y, retry, clearpath)
{
    [SPOILER]
    [COLOR=Navy][B][I]var[/I][/B][/COLOR] i, n;
    [COLOR=Navy][B][I]var[/I][/B][/COLOR] _teleport;
    [COLOR=Navy][B][I]var[/I][/B][/COLOR] _path;
    [COLOR=Navy][I][B]var[/B][/I][/COLOR] _retry = [COLOR=Red]0[/COLOR];

    [COLOR=Navy][B][I]if[/I][/B][/COLOR](x == me.x && y == me.y)
        [B][I][COLOR=Navy]return true[/COLOR][/I][/B];

    [COLOR=Navy][I][B]if[/B][/I][/COLOR](arguments.length < [COLOR=Red]4[/COLOR]) [COLOR=Green]// arguments.length entspricht der Anzahl der übergebenen Parameter[/COLOR]
        retry = [COLOR=Red]3[/COLOR];

    [COLOR=Navy][B][I]if[/I][/B][/COLOR](arguments.length < [COLOR=Red]5[/COLOR])
        clearpath = [COLOR=Navy][B][I]false[/I][/B][/COLOR];
    [/SPOILER]    
    [COLOR=Green]// ...[/COLOR]
}
Was uns hierbei eher interessieren soll, sind die Defaultparameter.
Ich schreibe mal ein paar Varianten, wie ich diese Funktion aufrufen würde und ihr dürft raten, welche davon korrekt sind:
Code:
[COLOR=Green]
// Variante 1:[/COLOR]
NTM_MoveTo([COLOR=Red]108[/COLOR], [COLOR=Red]7792[/COLOR], [COLOR=Red]5292[/COLOR], [COLOR=Red]2[/COLOR], [COLOR=Navy][B][I]true[/I][/B][/COLOR]);
[COLOR=Green]// Kann das so funktionieren?[/COLOR]
[SPOILER]
[COLOR=Green]/*
Ja, der Aufruf ist logisch wie syntaktisch korrekt.
*/[/COLOR]
[/SPOILER]
[COLOR=Green]// Variante 2:[/COLOR]
NTM_MoveTo([COLOR=Red]108[/COLOR], [COLOR=Red]7792[/COLOR], [COLOR=Red]5292[/COLOR], [COLOR=Red]2[/COLOR]);
[COLOR=Green]// Kann das so funktionieren?[/COLOR]
[SPOILER]
[COLOR=Green]/*
Ja, der Aufruf ist logisch wie syntaktisch korrekt.
*/[/COLOR]
[/SPOILER]
[COLOR=Green]// Variante 3:[/COLOR]
NTM_MoveTo([COLOR=Red]108[/COLOR], [COLOR=Red]7792[/COLOR], [COLOR=Red]5292[/COLOR]);
[COLOR=Green]// Kann das so funktionieren?[/COLOR]
[SPOILER]
[COLOR=Green]/*
Ja, der Aufruf ist logisch wie syntaktisch korrekt.
*/[/COLOR]
[/SPOILER]
[COLOR=Green]// Variante 4:[/COLOR]
NTM_MoveTo([COLOR=Red]108[/COLOR], [COLOR=Red]7792[/COLOR]);
[COLOR=Green]// Kann das so funktionieren?[/COLOR]
[SPOILER]
[COLOR=Green]/*
Nein, bei diesem Aufruf fehlen Parameter, für die keine Defaultwerte definiert sind.
*/[/COLOR]
[/SPOILER]
Offensichtlich sind auch einige Varianten korrekt, bei denen augenscheinlich Parameter fehlen. Dies liegt daran, dass für die letzen beiden Parameter retry und clearpath Defaultparameter definiert sind.
Übergebe ich eben diese Parameter nicht, werden die Variablen trotzdem initialisiert, sodass die Funktion mit ihnen arbeiten kann. Erst im letzten Beispiel lasse ich einen Parameter aus, für den (in diesem Fall sinnvollerweise) kein Defaultwert definiert ist.
Führt man die Funktion so aus, würde das sehr wahrscheinlich zu einem Ausnahmefehler führen, denn die Funktion bricht nicht automatisch ab, wenn einzelne Parameter nicht definiert sind.
Ich hoffe trotzdem, dass klar geworden ist, wie Defaultparameter in D2NT Funktionen aussehen und welchen Nutzen sie haben.
Hier nochmal die Funktion von oben mit ausführlichen Kommentaren:
Code:
[COLOR=Navy][B][I]function[/I][/B][/COLOR] NTM_MoveTo(areaid, x, y, retry, clearpath)
{
    [SPOILER]
    [COLOR=Green]// Hier werden alle Funktionseigenen Variablen deklariert[/COLOR]
    [COLOR=Navy][B][I]var[/I][/B][/COLOR] i, n;
    [B][I][COLOR=Navy]var[/COLOR][/I][/B] _teleport;
    [COLOR=Navy][B][I]var[/I][/B][/COLOR] _path;
    [COLOR=Navy][B][I]var[/I][/B][/COLOR] _retry = [COLOR=Red]0[/COLOR];

    [COLOR=Navy][B][I]if[/I][/B][/COLOR](x == me.x && y == me.y)[COLOR=Green] // Wenn die Zielkoordinaten den aktuellen Koordinaten des Charakters entsprechen - hier steckt das Objekt me drin, darauf gehen wir im nächsten Kapitel ein ;)[/COLOR]
        [B][I][COLOR=Navy]return[/COLOR][/I][/B] [COLOR=Navy][B][I]true[/I][/B][/COLOR]; [COLOR=Green]// Beenden der Funktion mit Rückgabewert true[/COLOR]

    [COLOR=Navy][B][I]if[/I][/B][/COLOR](arguments.length < [COLOR=Red]4[/COLOR]) [COLOR=Green]// Wenn weniger als 4 Parameter übergeben wurden, also nur areaid, x und y[/COLOR]
        retry = [COLOR=Red]3[/COLOR]; [COLOR=Green]// ...dann wird retry mit dem Defaultwert 3 initialisiert[/COLOR]

    [B][I][COLOR=Navy]if[/COLOR][/I][/B](arguments.length < [COLOR=Red]5[/COLOR])[COLOR=Green] // Wenn weniger als 5 Parameter übergeben wurden, also nur areaid, x, y und retry[/COLOR]
        clearpath = [COLOR=Navy][B][I]false[/I][/B][/COLOR];[COLOR=Green] // ... dann wird clearpath mit dem Defaultwert false initialisiert[/COLOR]
    [/SPOILER]    
    [COLOR=Green]// ...[/COLOR]
}
Benennung von Funktionen
Ich möchte an dieser Stelle nochmal auf die Bennenung von Funktionen, Parametern, sowie sonstigen Funktionsvariablen eingehen. Hier gibt D2NT eine gewisse Norm vor, an die man sich halten sollte, wenn man ungern lange nach Funktionen sucht und es schätzt, wenn man sich schnell zurechtfinden kann.
Funktionsnamen in D2NT beginnen immer mit den Buchstaben NT, gefolgt weiteren Großbuchstaben, die die zugehörigkeit zu dem Script der common library deutlich machen, in der die Funktion definiert ist. Das ist enorm sinnvoll, da die common library von D2NT aus einigen verschiedenen Scripten besteht, wovon die meisten in den eigentlichen Bot Scripten zum Einsatz kommen.
Da in den Bot Scripten selbst nur Funktionsaufrufe stattfinden, kann man dort die Funktionen selbst nicht einsehen. Dies muss man aber immer wieder, darum ist es wichtig, dass der Name einer Funktion auch direkt den Namen des common library Scripts indiziert, in dem es sich befindet.
Dies ermöglicht es, direkt im richtigen Script nach einer Funktionsdefinition zu suchen, sodass man es nicht auf gut Glück in einem der ~12 Scripte versuchen muss.

Um das zu verdeutlichen, nenne ich einfach mal einige Funktionsdefinitionen und nenne die Scripte, auf die die Namen der Funktionen verweisen:
Code:
[B][I][COLOR=Navy]function[/COLOR][/I][/B] NTM_MoveTo(areaid, x, y, retry, clearpath) [COLOR=Green]// NTM verweist auf: NTMove.ntl[/COLOR]
{
    [COLOR=Green]// ...[/COLOR]
}

[COLOR=Navy][I][B]function[/B][/I][/COLOR] NTT_GetCorpses() [COLOR=Green]// NTT verweist auf: NTTown.ntl[/COLOR]
{
    [COLOR=Green]// ...[/COLOR]
}

[COLOR=Navy][I][B]function[/B][/I][/COLOR] NTTMGR_VisitAkara()[COLOR=Green] // NTTMGR verweist auf: NTTownManager.ntl[/COLOR]
{
    [COLOR=Green]// ...[/COLOR]
}

[COLOR=Navy][B][I]function[/I][/B][/COLOR] NTSI_LoadNIPFiles(filepath) [COLOR=Green]// NTSI verweist auf: NTSnagIt.ntl[/COLOR]
{
    [COLOR=Green]// ... [/COLOR]
}

[B][I][COLOR=Navy]function[/COLOR][/I][/B] NTA_ClearPosition(range, pickitem, safelevel) [COLOR=Green]// NTA verweist auf: NTAttack.ntl[/COLOR]
{
   [COLOR=Green] // ... [/COLOR]
}

[COLOR=Navy][B][I]function[/I][/B][/COLOR] NTC_IncludeConfig(filepath) [COLOR=Green]// NTC verweist auf: NTCommon.ntl[/COLOR]
{
    [COLOR=Green]// ... [/COLOR]
}
Man kann also auf den ersten Blick sehen, wo man nach einer Funktion zu suchen hat. Natürlich muss die Bennenung nur bei Funktionen in Scripten der common library erfolgen, nicht bei speziellen Funktionen, die in Botscripten selbst definiert sind.

Alle Funktionseigenen Variablen beginnen mit einem Unterstrich, sodass man sie klar von den Parametern der Funktion unterscheiden kann, welche diesen Unterstrich nicht aufweisen. Ich gebe hierzu kein Beispiel, man kann es z.B. im Beispiel weiter oben sehen, aber auch in jeder anderen D2NT Funktion.
Muddy Waters is offline  
Thanks
30 Users
Old 06/29/2010, 20:37   #3
Administrator
 
Muddy Waters's Avatar
 
elite*gold: 41624
Join Date: Jan 2010
Posts: 22,728
Received Thanks: 12,654
5. Klassen und Objekte

Und damit sind wir im letzten und vielleicht schwierigsten Teil der Grundlagen angelangt, nämlich bei den Klassen und Objekten bzw. der Objektorientierung. Man möge mir verzeihen, dass ich bewusst einige Teile auslasse, da man sie im Bezug auf D2NT wenig bis nie braucht.
Unter Objektorientierung versteht man die Beschreibung eines Systems mithilfe von Objekten. Typischerweise hat man also irgendeinen Sachverhalt (mehr oder weniger) aus dem wahren Leben und versucht diesen möglichst geschickt in Objekten zu klassifizieren.

Doch was ist überhaupt ein Objekt in der Programmierung?
Nun, es ist gar nicht so leicht dies konrekt in Worte zu fassen, weil Objekte sehr verschieden aussehen können, also versuchen wir uns auf das zu beschränken, was alle Objekte gemeinsam haben:
Alle Objekte verfügen über Attribute (== Eigenschaften in Form von einfachen Variablen oder anderen Objekten) und Methoden (== Funktionen, die auf die Attribute zurückgreifen können).
Methoden bezeichnet man auch als Elementfunktionen, die Begriffe könnte ihr aber als synonym ansehen.

Da das alles immernoch etwas abstrakt daherkommt, verdeutliche ich es an einem einfachen Beispiel: Ein Sportwagen

Wir betrachten nun einen Sportwagen und versuchen ihn zu klassifizieren. Das könnte dann in groben Auszügen beispielsweise so aussehen:
Attribute:
  • Modellbezeichnung
  • Motorleistung
  • Höchstgeschwindigkeit
  • Türenanzahl
  • Reifen
  • etc.

Folglich könnte man sich auch einige Methoden überlegen:
  • Beschleunigen()
  • Bremsen()
  • LiegenBleiben()
  • etc.

Man sieht hier sehr schön, dass zu den Attributen auch Objekte gehören können. So könnten die Reifen beispielsweise Objekte sein, die über Luftdruck, Profiltiefe und Umfang verfügen und ausserdem über eine Methode Drehen() verfügen.

Ich denke man kann an diesem Beispiel sehr gut nachvollziehen, wie man sich ein Objekt generell vorstellen kann.

Was vesteht man unter dem Begriff Klasse?

Eine Klasse ist eng mit dem Objekt verknüpft, denn sie ist nicht weniger als der Bauplan für ein Objekt.
Einem Objekt liegt also immer eine Klasse zurgunde, in der genau definiert ist, welche Attribute das Objekt hat und wie die Methoden aussehen, sprich wie die Funktionen konkret aufgebaut sind bzw. ablaufen.
Um also ein Objekt anzulegen, genauer gesagt eine Instanz eines Objekts zu erzeugen, muss der Bauplan dazu in Form einer entsprechenden Klasse gegeben sein.

Zum Instantiieren eines Objektes brauchen wir also zum einen den Bauplan, ausserdem müssen wir aber bei der Erzeugung auch die Attribute des Objekts festlegen.
Irgendwoher muss unser Sportwagen Objekt schließlich wissen, dass es ein Ferrari sein soll und gefälligst rot zu sein hat.

Nun sind wir etwas vom eigentlichen Thema - nämlich der D2NT Programmierung - abgedriftet; Das hat aber seinen Grund, denn im Gegensatz zum letzten Beispiel, das man sich ja noch relativ gut vorstellen konnte, sind die typischen Objekte in D2NT meist ein bisschen abstrakter.

In diesem Sinne bewegen wir uns nun mal wieder etwas auf das eigentliche Thema zu...
Ich darf an dieser Stelle schonmal eine kleine Entwarnung loszuwerden: In D2NT hat man zwar mit sehr vielen Objekten zu tun, allerdings nimmt uns die Laufzeitumgebung in den meisten Fällen die eigentliche Instantiierung der Objekte ab.
Sie übergibt uns stattdessen direkt eine fertige Instanz, bei der alle Attribute durch die Laufzeitumgebung initialisiert wurden, sodass wir direkt mit dem Objekt arbeiten können.

So schön das vielleicht klingen mag, es hat auch seine Schattenseiten: Die Methoden, sowie die zugrunde liegenden Klassen sind in der Laufzeitumgebung definiert, sodass es für uns keine Möglichkeit gibt, diese einzusehen.
Wir haben also Objekte, können uns aber nicht anschauen, nach welchem Bauplan diese aufgebaut sind.
Dies ist ein enormer Nachteil, da man so keine Möglichkeit hat, die Klassendefinitionen einzusehen, um sich so über den Aufbau eines Objekts klar zu werden.
Stattdessen darf man in vielen Fällen selber probieren und/oder in anderen Scripten nach Referenzen ausschau halten, was mitunter sehr nervenaufreibend werden kann.

Aber wo genau finden sich bei D2NT Objekte?
Eigentlich fast überall, ich nenne einfach mal ein paar Beispiele für Objekte: Da wären Monster, Items, (weltliche) Objekte (Schreine, Truhen, unbenutzbare Dinge in der Landschaft), NPCs und nicht zuletzt der eigene Char

Wir gehen einfach mal auf den eigenen Char ein.
Dieser wird durch das globale Objekt me beschrieben.
Global heisst in diesem Fall, dass immer eine Instanz dieses Objektes existiert, welche in jedem eurer Scripte gültig bzw. bekannt ist.

Hier einmal auszugsweise ein paar Attribute und Methoden des Objekts me inklusive kurzer Erläuterungen:
Code:
[COLOR=Green]// Attribute des me Objekts:[/COLOR]
[I]classid[/I]     [COLOR=Green]// Die ID der Charakterklasse z.B. 0 für Amazonen oder 4 für Barbaren[/COLOR]
[I]name[/I]         [COLOR=Green]// Der Name des Charakters[/COLOR]
[I]hp[/I]            [COLOR=Green]// Die aktuelle Menge an Hitpoints[/COLOR]
[I]mp[/I]            [COLOR=Green]// Die aktuelle Menge an Mana[/COLOR]
[I]hpmax[/I]        [COLOR=Green]// Die maximal mögliche Menge an Hitpoints[/COLOR]
[I]mpmax[/I]        [COLOR=Green]// Die maximal mögliche Menge an Mana[/COLOR]
[I]act[/I]            [COLOR=Green]// Die Nummer des Aktes, in der sich der Char aktuell befindet[/COLOR]
[I]areaid[/I]        [COLOR=Green]// Die ID der Ebene, in der sich der Char gerade aufhält[/COLOR]
[I]x[/I]           [COLOR=Green] // Die x-Koordinate der aktuellen Position[/COLOR]
[I]y[/I]            [COLOR=Green]// Die y-Koordinate der aktuellen Position[/COLOR]
[I]gamename[/I]    [COLOR=Green]// Der Name des Spiels, in dem sich der Char aktuell befindet[/COLOR]
[I]gamepassword[/I][COLOR=Green]// Das Passwort des Spiels, in dem sich der Char aktuell befindet[/COLOR]
[I]ingame[/I]        [COLOR=Green]// Ob sich der Char aktuell in einem Spiel befindet
//[...][/COLOR]
[COLOR=Green]// Methoden des me Objekts[/COLOR]
[I]cancel()[/I]    [COLOR=Green]// Dient zum Abbruch von Dialogen und dem Schließen von Fenstern[/COLOR]
[I]GetStat()[/I]    [COLOR=Green]// Gibt die Höhe bestimmter Eigenschaftswerte aufgrund deren ID wieder (z.B. Stärke, Resistenzen...)[/COLOR]
[I]GetState()[/I]    [COLOR=Green]// Prüft auf Vorhandensein eines bestimmten Zustandes mithilfe der jeweiligen ID (z.B. Flüche, Auren, Vergiftung...)[/COLOR]
[I]GetItems()[/I]    [COLOR=Green]// Liefert Item Objekte für alle Items die sich auf dem Char befinden in Form eines Arrays zurück[/COLOR]
[I]ClickItem()[/I]    [COLOR=Green]// Führt einen Klick auf ein angegebenes Item aus
/* 
    Da diese Liste nur der Demonstration dienen soll habe ich auf deren formale Parameter verzichtet, 
    was nicht heissen soll, dass diese Funktionen keine Parameter zum Aufruf benötigen, im Gegenteil, 
    meist sind die Methoden überladen und weisen somit mehrere mögliche Parametersignaturen auf.
*/[/COLOR]
Der eigentliche Zugriff auf Attribute oder Methoden findet immer mit dem Punktoperator statt.
Hier mal einige Beispiele:
Code:
[COLOR=Navy][B][I]var[/I][/B][/COLOR] _myCharname = me.charname; [COLOR=Green]// Die Variable _myCharname wird mit dem Wert des Attributes charname des Objekts me initialisiert[/COLOR]

[COLOR=Navy][B][I]if[/I][/B][/COLOR](me.classid == [COLOR=Red]0[/COLOR]) [COLOR=Green]// Wenn das Attribut classid des Objekts me gleich 0 ist, also wenn es sich bei dem Char um eine Amazone handelt[/COLOR]
{
    // Do something...
}
[COLOR=Navy][B][I]
var[/I][/B][/COLOR] _items = me.GetItems(); [COLOR=Green]// Wir versuchen ein Array bestehend aus Objekten für alle Items auf unserem Char zu bekommen[/COLOR]

[COLOR=Navy][B][I]if[/I][/B][/COLOR](_items) [COLOR=Green]// Wenn dieses Array existiert[/COLOR]
{
    [COLOR=Navy][B][I]for[/I][/B][/COLOR]([COLOR=Navy][B][I]var[/I][/B][/COLOR] i = [COLOR=Red]0[/COLOR]; i < _items.length; i++) [COLOR=Green]// Durchlaufen aller Objekte[/COLOR]
    {
        [COLOR=Navy][B][I]if[/I][/B][/COLOR](_item[i].classid == [COLOR=Red]200[/COLOR]) [COLOR=Green]// Wenn die ID des gerade betrachteten Items gleich 200 ist (eine Beserker Axt)[/COLOR]
        {
            [COLOR=Green]// Do something...[/COLOR]
        }
    }
}
Wie man im letzten Teil des Beispiels sieht kann man auch Problemlos Objekte in Arrays speichern. Zunächst wird dazu wie von Arrays gewohnt ein Element indiziert, in diesem Fall mit dem Index i. Danach werden dann mit dem Punktoperator die Attribute des Objekts an der Stelle i im Array angesprochen.
Man sieht in dem Beispiel noch etwas interessantes: nämlich die Abfrage, ob ein Objekt, in diesem Fall sogar ein Array aus Objekten, überhaupt existiert. Das ist enorm wichtig, denn wenn ihr den Punktoperator auf Elemente anwendet, die undefiniert sind, führt das in aller Regel zu einem Ausnahmefehler gefolgt vom D2 Absturz.
Darum gehört es nicht nur zum guten Stil, es ist auch einfach extrem Sinnvoll beim Anfordern von Objekten immer zunächst zu überprüfen, ob ein Objekt existiert, bevor ihr auf dessen Attribute zugreift.

Man sieht hier sogar noch ein kleines Detail, was ich zuvor schonmal angemerkt hatte: Auch Arrays sind (in JavaScript) Objekte.
Das seht ihr an dem Zurgriff auf das Attribut length des Arrays, welches immer die aktuelle Anzahl der im Array vorhandenen Elemente angibt.
Genauso sind auch alle Zeichenketten Objekte, was man beispielsweise an Attributen wie length oder Methoden wie ToLowerCase() erkennt

Insgesamt bietet JavaScript eine vielzahl an Objekten. Diese Objekte werden dabei in den meisten Fällen mithilfe des new Operators instantiiert.
Auch wenn das beim Programmieren von D2NT Funktionen eigentlich eher selten benötigt wird. In erster Linie sind es in diesem Zusammenhang Array und Date Objekte, welche wie folgt instantiiert werden:
Code:
[COLOR=Navy][B][I]var[/I][/B][/COLOR] _myArray = [B][I][COLOR=Navy]new[/COLOR][/I][/B] Array([COLOR=Red]5[/COLOR]);[COLOR=Green] // Ein Array bestehend aus 5 Feldern
// Das aktuelle Datum sei der 02.09.2010 20:00:00[/COLOR]
[COLOR=Navy][B][I]var[/I][/B][/COLOR] _myDate = [COLOR=Navy][B][I]new[/I][/B][/COLOR] Date(); [COLOR=Green]// Ein Date Objekt; Übergeben wir bei der Instantiierung keine Parameter, werden die Attribute mit der aktuellen Uhrzeit und dem aktuellen Datum  initialisiert[/COLOR]

[COLOR=Navy][B][I]if[/I][/B][/COLOR](_myDate) [COLOR=Green]// Wenn das Objekt nun existiert, können wir das aktuelle Datum auf die Konsole ausgeben lassen, indem wir es vorher mit der passenden Methode in eine Zeichenkette umwandeln:[/COLOR]
    Print([COLOR=DimGray]"Aktuelles Datum: "[/COLOR] + Date.toLocaleFormat([COLOR=DimGray]"%m/%d/%y %H:%M:%S"[/COLOR])); [COLOR=Green]// Die Ausgabe lautet:: Aktuelles Datum 09/02/2010 20:00:00[/COLOR]
Zum Abschluss schauen wir uns nun nochmal an, wie man in D2NT typischerweise Objekte instantiiert, auch wenn dieser Ausdruck eigentlich nur teilweise passt.
In der Regel kommt hierbei die Funktion NTC_FindItemUnit() der common library zum Einsatz.

Dazu werden wir jetzt einfach eine kleine Beispielfunktion schreiben.
Aufgabe dieser Funktion soll es sein, die nähere Umgebung auf ein Objekt Namens "Chest" zu untersuchen und dieses gegebenenfalls zu öffnen.
Ich merke an dieser Stelle mal an, dass diese Funktion anschaulich sein und nicht als sonderlich elegante Lösung dienen soll, diese sähe vermutlich etwas anders aus.
Code:
[COLOR=Navy][B][I]function[/I][/B][/COLOR] MWT_OpenNearbyChests(range) [COLOR=Green]// Parameter der Funktion soll ein maximaler Abstand der Truhe zum Char sein[/COLOR]
{
    [COLOR=Navy][B][I]var[/I][/B][/COLOR] _objects; [COLOR=Green]// Wir deklarieren eine Variable, die die Liste mit den in der Nähe befindlichen Objekten aufnehmen soll[/COLOR]
    
    [COLOR=Navy][B][I]if[/I][/B][/COLOR](arguments.length < [COLOR=Red]1[/COLOR]) [COLOR=Green]// Wurde kein Parameter übergeben...[/COLOR]
        range = [COLOR=Red]20[/COLOR]; [COLOR=Green]// ...wird range mit dem Wert 20 initialisiert[/COLOR]
        
    _objects = NTC_FindUnit(NTC_UNIT_OBJECT); [COLOR=Green]// Wir fordern eine Liste aus Objekten des Typs NTC_UNIT_OBJECT an, also weltlichen Objekten wie Truhen oder Schreinen, wir suchen schließlich eine Truhe[/COLOR]
    
    [COLOR=Navy][B][I]if[/I][/B][/COLOR](_objects) [COLOR=Green]// Kontrolle, ob die Liste aus Objekten existiert[/COLOR]
    {
        [COLOR=Navy][I][B]do[/B][/I][/COLOR]
        {
            [COLOR=Navy][B][I]if[/I][/B][/COLOR](_objects.name == [COLOR=DimGray]"Chest"[/COLOR] && range <= GetDistance(me, _objects)) [COLOR=Green]// Wenn die Zeichenkette, die im Attribut name unseres Objektes gespeichert ist, gleich der Zeichenkette "Chest" ist UND die Distanz zum Objekt kleiner als unsere angebene Maximalreichweite ist[/COLOR]
            {
                [COLOR=Navy][B][I]if[/I][/B][/COLOR](NTC_OpenChest(_objects)) [COLOR=Green]// Wir rufen die Funktion zum Öffnen von Truhen auf; Diese erwartet als Parameter das das Objekt einer zu öffnenden Truhe[/COLOR]
                    NTSI_PickItems(); [COLOR=Green]// Wenn das Öffnen erfolgreich war, heben wir alle in der Nähe befindlichen Gegenstände auf, die gut sein könnten[/COLOR]
            }
        } [COLOR=Navy][B][I]while[/I][/B][/COLOR](_objects.GetNext()); [COLOR=Green]// Wir springen mit der Methode GetNext()  zum nächsten Element in der Liste; Sind wir am Ende der Liste angelangt, ist der Rückgabewert der Nullzeiger, sodass die Schleife beendet wird[/COLOR]
    }
}
Wie man sieht sind Objekte wirklich verbreitet: Viele der Standardfunktionen akzeptieren als Parameter Objekte, was uns die Sache natürlich leicht macht, weil wir statt vielen verschiedenen Werten, die den Attributen entsprechen nurnoch ein Objekt übergeben müssen, welches all diese Werte beinhaltet.

Ich empfehle zur weiteren Vertiefung einfach mal ein bisschen in den Funktionen der common libary zu lesen. Die meisten Funktionen sind sehr simpel und schnell verstanden und meiner Erfahrung nach bringt es Anfängern Sicherheit, wenn man sehen kann, wie es gemacht wird.
Zum Schluss möchte ich noch einmal kurz die formalen Parameter der Funktion NTC_FindUnit() bennenen, sowie die Konstanten für die verschiedenen Objekttypen:
Code:
NTC_FindUnit(unittype, search, retry);
[COLOR=Green]/*
unittype  - Der Typ des zu suchenden Objektes (siehe unten)
search       -    Ein Suchbegriff der das Objekt beschreibt; Das kann beispielsweise ein Name sein, eine classid oder im wohl genauesten Fall eine GID)
retry       - Wieviele Versuche unternommen werden sollen, um das Objekt zu finden.
Alle Parameter haben Default Werte, es steht euch also frei die Funktion mit einem, zwei oder drei Parametern aufzurufen, ein Aufruf ohne Parameter wäre jedoch fehlerhaft und die Funktion würde den Nullzeiger zurückgeben.
*/[/COLOR]
[COLOR=Green]// Die verschiedenen Arten von Objekten[/COLOR]
[B][COLOR=Navy][I]const[/I][/COLOR][/B] NTC_UNIT_PLAYER = [COLOR=Red]0[/COLOR];
Code:
[SIZE=4][SIZE=2][B][COLOR=Navy][I]const[/I][/COLOR][/B][/SIZE][/SIZE][SIZE=4][SIZE=2] NTC_UNIT_NPC = [COLOR=Red]1[/COLOR];
[/SIZE][/SIZE][SIZE=4][SIZE=2][B][COLOR=Navy][I]const[/I][/COLOR][/B][/SIZE][/SIZE][SIZE=4][SIZE=2] NTC_UNIT_MERC = NTC_UNIT_NPC;
[/SIZE][/SIZE][SIZE=4][SIZE=2][B][COLOR=Navy][I]const [/I][/COLOR][/B][/SIZE][/SIZE][SIZE=4][SIZE=2]NTC_UNIT_MONSTER = NTC_UNIT_NPC;
[/SIZE][/SIZE][SIZE=4][SIZE=2][B][COLOR=Navy][I]const[/I][/COLOR][/B][/SIZE][/SIZE][SIZE=4][SIZE=2] NTC_UNIT_OBJECT = [COLOR=Red]2[/COLOR];
[/SIZE][/SIZE][SIZE=4][SIZE=2][B][COLOR=Navy][I]const[/I][/COLOR][/B][/SIZE][/SIZE][SIZE=4][SIZE=2] NTC_UNIT_MISSILE = [COLOR=Red]3[/COLOR];
[/SIZE][/SIZE][SIZE=4][SIZE=2][B][COLOR=Navy][I]const[/I][/COLOR][/B][/SIZE][/SIZE][SIZE=4][SIZE=2] NTC_UNIT_ITEM = [COLOR=Red]4[/COLOR];
[/SIZE][/SIZE][SIZE=4][SIZE=2][B][COLOR=Navy][I]const[/I][/COLOR][/B][/SIZE][/SIZE][SIZE=4][SIZE=2] NTC_UNIT_TILE = [COLOR=Red]5[/COLOR];
[/SIZE][/SIZE][SIZE=4][SIZE=2][B][COLOR=Navy][I]const[/I][/COLOR][/B][/SIZE][/SIZE][SIZE=4][SIZE=2] NTC_UNIT_STASH = [COLOR=Red]267[/COLOR];
[/SIZE][/SIZE][SIZE=4][SIZE=2][B][COLOR=Navy][I]const [/I][/COLOR][/B][/SIZE][/SIZE][SIZE=4][SIZE=2]NTC_UNIT_CUBE = [COLOR=Red]549[/COLOR];
[/SIZE][/SIZE]
Wie man schon am Präfix NTC erkennt, könnt ihr die Funktion und die Konstanten in der libary NTCommon.ntl wiederfinden.

Ich habe mich in diesem Kapitel bewusst dafür entschieden, nicht auf die Konstruktion von Klassen und das Thema Verebung einzugehen.
Aus meiner sicht muss man bei der D2NT Programmierung in den seltensten Fällen selber Klassen Konstruieren, weshalb das Thema eventuell mehr Verwirrung stiften, als wirklich nutzen würde.
Solltet ihr dennoch der Meinung sein, ihr bräuchtet unbedingt eigene Klassen, dann fragt einfach in einem entsprechenden topic in der Programming Sektion, dazu ist sie schließlich da.

Muddy Waters is offline  
Thanks
25 Users
Old 06/29/2010, 20:38   #4
Administrator
 
Muddy Waters's Avatar
 
elite*gold: 41624
Join Date: Jan 2010
Posts: 22,728
Received Thanks: 12,654
// Reservierung 3 - Damit sollte ich auskommen
Muddy Waters is offline  
Thanks
9 Users
Old 06/29/2010, 20:54   #5
 
sataan1337's Avatar
 
elite*gold: 0
Join Date: Dec 2008
Posts: 628
Received Thanks: 108
Quote:
Originally Posted by Muddy_Waters View Post
// Reservierung 3 - Damit sollte ich auskommen
dann kann ich hier ja schonmal danke sagen ^^

schon länger nach nem guide für d2nt gesucht aber nix passendes gefunden

*edit: nach langer Zeit auch mal den Danke-Button nachgereicht xD
wie war ich denn damals drauf o.O
sataan1337 is offline  
Old 06/29/2010, 21:11   #6
 
kal_el's Avatar
 
elite*gold: 0
Join Date: Jan 2009
Posts: 7,310
Received Thanks: 2,205
sticky ^^
kal_el is offline  
Old 06/29/2010, 23:08   #7
 
elite*gold: 0
Join Date: Apr 2006
Posts: 6,597
Received Thanks: 1,830
gute idee

vllt. sollt ich mal anfangen die d2nt api zu dokumentieren. aber hmm das ist schon sehr viel arbeit
Medix is offline  
Old 06/30/2010, 00:59   #8
 
elite*gold: 0
Join Date: May 2010
Posts: 212
Received Thanks: 40
Danke für Bisheriges und im Voraus.
Bisher konnte ich folgen :-))
fred9x9 is offline  
Old 06/30/2010, 15:08   #9
Administrator
 
Muddy Waters's Avatar
 
elite*gold: 41624
Join Date: Jan 2010
Posts: 22,728
Received Thanks: 12,654
Quote:
Originally Posted by sataan1337 View Post
dann kann ich hier ja schonmal danke sagen ^^

schon länger nach nem guide für d2nt gesucht aber nix passendes gefunden
Bitteschön!

Quote:
Originally Posted by Medix View Post
gute idee

vllt. sollt ich mal anfangen die d2nt api zu dokumentieren. aber hmm das ist schon sehr viel arbeit
In der Tat, obwohl ein grober Überblick mit den wichtigsten globalen Funktionen und Objekten im Bereich des Machbaren liegen sollte.

Es reicht ja, wenn dort die Dinge stehen, die nicht ganz so offensichtlich sind.
Quote:
Originally Posted by fred9x9 View Post
Danke für Bisheriges und im Voraus.
Bisher konnte ich folgen :-))
Sehr schön, das zeigt mir, dass ich auf dem richtigen Weg bin.

Es ist schwierig sich einerseits auf das wesentliche zu beschränken, gleichzeitig aber noch nachvollziehbar zu bleiben.
Wenn an der ein oder anderen Stelle Fragen offen bleiben, bitte ich um Rückmeldung.

Lg
Muddy
Muddy Waters is offline  
Thanks
5 Users
Old 07/02/2010, 11:26   #10
 
Diablofarmer's Avatar
 
elite*gold: 0
Join Date: Sep 2009
Posts: 1,456
Received Thanks: 290
Junge du bist krass ! mitten in den Prüfungen und setzt sich bei 35°C noch hin um guides zu schreiben, alter schwede
*Respekt und Dankeeeeeeeeeesehr*
Diablofarmer is offline  
Old 07/06/2010, 19:07   #11
Administrator
 
Muddy Waters's Avatar
 
elite*gold: 41624
Join Date: Jan 2010
Posts: 22,728
Received Thanks: 12,654
Quote:
Originally Posted by Diablofarmer View Post
Junge du bist krass ! mitten in den Prüfungen und setzt sich bei 35°C noch hin um guides zu schreiben, alter schwede
*Respekt und Dankeeeeeeeeeesehr*
Das Wetter hat sich ja glücklicherweise etwas gelegt.
Und ich hab die nächste Prüfung erst am Montag...Zeit für ein paar Updates.

Kritik und allgemeines Feedback sind natürlich weiterhin gern gesehen.

Lg
Muddy
Muddy Waters is offline  
Thanks
1 User
Old 07/07/2010, 10:38   #12
 
elite*gold: 0
Join Date: Nov 2009
Posts: 67
Received Thanks: 8
Liest sich alles sehr gut, vielen Dank dafür. Vielleicht wäre so ne Art "Beispielprojekt/-skript" eine Überlegung wert? Um quasi aufzuzeige, an welchen Stellen man die einzelnen Sachen dann einsetzen würde.
jAmMyX is offline  
Old 07/07/2010, 11:03   #13
Administrator
 
Muddy Waters's Avatar
 
elite*gold: 41624
Join Date: Jan 2010
Posts: 22,728
Received Thanks: 12,654
Quote:
Originally Posted by jAmMyX View Post
Liest sich alles sehr gut, vielen Dank dafür. Vielleicht wäre so ne Art "Beispielprojekt/-skript" eine Überlegung wert? Um quasi aufzuzeige, an welchen Stellen man die einzelnen Sachen dann einsetzen würde.
Das werde ich ganz zum Schluss einfügen.

In der Regel kommt dort ja alles vor, was uns JavaScript bietet, deshalb müssen die Teile, die zu den elementaren Grundlagen gehören, vorher klar sein, damit man auch alles gut nachvollziehen kann.

Ich hatte beispielsweise daran gedacht, zu zeigen, wie man Scripte zum säubern jeder beliebigen Ebene schreiben kann. Aber dazu müssen eben Grundlegende Dinge wie die Bedeutung und der Ablauf von Funktionen klar sein. Um zu verstehen, was in den Funktionen selbst passiert, muss man dann auch noch über Klassen und Objekte bescheid wissen.

Das ist auch der Grund, warum ich in den meisten Beispielen auf konkrete D2NT Scripte oder Auszüge davon verzichte. Auch wenn die Algorithmen im einzelnen nicht schwer sein müssen, sind eigentlich immer diverse Funktionen und Objekte vorhanden. Und einfache Variablen über komplexe Variablen zu erklären scheint nicht wirklich sinnvoll.

Ich werde mir aber definitiv noch weitere Beispiele überlegen, die dann ganz am Ende auftauchen. Dort weiss ich dann, was ich dem geneigten Leser zutrauen kann bzw. was jetzt an Grundwissen vorhanden sein müsste und kann entsprechend in die Vollen gehen.

Lg
Muddy
Muddy Waters is offline  
Thanks
1 User
Old 07/22/2010, 02:30   #14

 
RezChams's Avatar
 
elite*gold: 0
Join Date: Mar 2009
Posts: 24,265
Received Thanks: 15,362
Ich Drucks mir aus :P und werde es mir am Wochenende etwas mehr einprägen
Danke für diesen post, Wollte ich eh mal lernen
RezChams is offline  
Old 07/29/2010, 22:28   #15
 
HolyWooD's Avatar
 
elite*gold: 0
Join Date: May 2009
Posts: 9
Received Thanks: 1
Sehr schönes Tutorial, hatte sowas schon lange mal gesucht.
PM wegen einem Fehler ist an dich raus

MfG HolyWooD
HolyWooD is offline  
Reply


Similar Threads Similar Threads
[Guide] C++ Programmierung mit Lua 5.1
05/28/2011 - Coding Tutorials - 9 Replies
Achtung dieser TUT ist aus m m o r p g - c o r e. Da ich dort mit Chaosduckman ein und die selbe Person bin sollte das klar gehen. Hi^^ Ich weiß das es für dieses Thema sehr viele Guides in google gibt, aber ich wollte mal eins zu elitepvpers bringen. Da das hier mein erster Guide ist freue ich mich über Feedback und Verbesserungs Vorschläge :-) Aber nun fangen wir an: 1. History and Informations 1-1 Was ist Lua?
[Guide] Grundlagen der IT / EDV
11/10/2009 - Tutorials - 7 Replies
1. Hardware Hardware sind die Geräte des Computers – alles, was man angreifen kann: Monitor, Drucker, Maus, Computer, Festplatte, ... 1.1. Eingabegeräte sind Geräte zur Eingabe von Daten ->Maus, Tastatur, ->Touchpad: zum Bewegen des Cursors bei Notebooks ->Touchscreen: Berührungsempfindlicher Monitor, der mit Finger oder Stift bedient wird. Anwendung: Fahrplanauskunft auf dem Bahnhof, Informationsbildschirme für Touristen, Geldausgabeautomaten in Banken. ->Ein Touchscreen ist ein...



All times are GMT +1. The time now is 17:52.


Powered by vBulletin®
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Support | Contact Us | FAQ | Advertising | Privacy Policy | Terms of Service | Abuse
Copyright ©2026 elitepvpers All Rights Reserved.