Kommunikation basiert auf dem Austausch von Daten. Entscheidungen werden mit ja – nein gefällt. Die Kühlschranktemperatur ist meist ein Wert zwischen 0 und 5 Grad Celsius. Einem Menschen werden Reihungen von Zeichen zur Identifikation mitgegeben. Diese Art der Aufladung von Gegenständen und Individuen mit Informationen kann auch in Programmiersprachen verwendet werden. Belegt wird dabei immer eine Variable mit einem Wert – eine Schublade bekommt ihren Inhalt. Mit dem Wissen den Namen der Schublade zu kennen ist deren Inhalt abfrag- und veränderbar. In der zweiten Stunden haben wir bereits mit der Mausinteraktion vier solcher Variablen/Schubladen kennengelernt. Die Processingumgebung stellt uns die Koordinaten der Maus über die Ausdrücke mouseX und mouseY zur Verfügung. Da sich hinter diesen Begriffen keine einfachen Klammern befinden werden sie als Variablen identifiziert. Processing ändert für uns vor jedem draw() Durchlauf den Wert dieser Variablen, sodass wir immer auf die aktuelle Lage der Maus zugreifen können.
Zum Einsteigen gebrauchen wir drei elementare Typen. Sie unterscheiden sich in Ihrem Fassungsvermögen – welche Art von Inhalten in ihnen abgelegt/gespeichert werden kann:
Bevor wir eine Variable nutzen können müssen wir sie ins Leben rufen; Processing mitteilen das wir vorhaben mit ihr im späteren Ablaufen zu arbeiten. Dies geschieht immer mit der Angabe ihres Typs (z.B. int, float, etc. ) und einem Namen zur Identifikation.
int ganzzahl; float gleitzahl; String textabschnitt;
Namen von Variablen dürfen nur Buchstaben, Zahlen, $ und _ enthalten und sollten immer mit einem Buchstaben beginnen. Die Vergaben von Leerzeichen ist nicht erlaubt. Um zusammengesetzte Name trotzdem lesbar abzubilden wird der erste Buchstabe eines folgenden Wortes großgeschrieben (z.B. roteaepfel wird zu roteAepfel). Genau wie beim Aufruf von Befehlen ist Groß- und Kleinschreibung unbedingt zu beachten!
Im oberen Beispiel haben wir zwar drei Variablen unterschiedliche Typs erstellt – jedoch können wir ohne Inhalt nicht mit ihnen arbeiten. Wir benötigen eine sogenannte Zuweisung eines Wertes. Dies geschieht mit einem = welches dem Variablennamen folgt. Rechts neben dem Istgleich erwartet Processing den Wert den es in hinter unserem Begriff (Variable) verstecken soll – gefolgt von einem Semikolon, um die Anweisung zu Beenden.
// ganzzahlige Variable mit dem Wert 40 int ganzzahl = 40; // Variable mit dem gleitkomma Inhalt 36.9 float gleitzahl = 36.9; // Variable die den Textschnipsel "Creative // Computing" beinhaltet String textabschnitt = "Creative Computing";
Beim Ausführen der beiden oberen Beispiele erhalten wir momentan keinerlei Feedback vom Programm. Das Sketchfenster öffnet sich und stellt uns den üblichen grauen Hintergrund dar. Das Einsehen des Wertes/Status der Variable könnte über eine textuelle Ausgabe in unserer Zeichenfäche geschehen, wäre aber vom Aufwand hoch einzustufen. In Processingprogramm existiert unter dem Texteditor, in welchem wir unseren Quellcode schreiben, eine schwarze Box. Bisher wurden uns hier im Ernstfall Fehlermedldungen abgebildet – zwei Befehlen erlauben uns aber auch sie selbst zu Befüllen:
// erstellt eine ganzzahlige Variable names // "zahl" und gibt ihr den Wert 39 int zahl = 39; // Ausgabe in der Konsole println (zahl); // erhöhe den Wert von "zahl" um eins zahl = zahl + 1; // Ausgabe in der Konsole println (zahl);
Die Angabe des Datentypen ist nur bei der Initialisierung nötig. Im weiteren Ablauf geschehen Abfragen und Zuweisungen ohne Angaben wie int, float oder String.
void draw() { // lösche die Zeichenfläche und fülle sie mit weiss background (255, 255, 255); // erstelle eine Variable mit dem Namen "grenze" // und gib ihr den ganzzahligen Wert 50 int grenze = 50; // wenn die Mausposition auf der x-Achse kleiner // ist als unsere definierte Grenze... if (mouseX < grenze) { // zeichne eine kleine ellipse an der Mausposition ellipse (mouseX, mouseY, 10, 10); }else{ // zeichne eine große ellipse an der Mausposition ellipse (mouseX, mouseY, 50, 50); } // dieser Teil wird immer ausgeführt // (unabhängig vom Mauszustand) rect (90, 90, 10, 10); }
Der Titel dieses Abschnittes lässt Erinnerungen an vergangene Zeiten im unbeliebten Mathekurs aufkommen. Visuelle Eigenschaften wie Positionen und Farbwerte werden von uns durch Zahlen angegeben – nun lernen wir diese Werte zu kontrollieren. Processing gibt uns dabei die vier grundlegenden Rechenoperationen, plus zwei Weitere die uns das Tippen ersparen.
float a = 4.2; float b = 1.3; // Ausgabe: 5.5 println (a + b); // Ausgabe: -2.8999999 println (b - a); // Ausgabe: "Creative Coding 1" println ("Creative " + "Coding " + 1);
int a = 5; int b = 2; // Ausgabe: 10 println (a * b); // Ausgabe: 2 println (a / b);
Das Ergebniss der Multiplikation von 5 x 2 = 10 ist zu wie erwartet eingetreten. Aber bei der Division beider Werte fehlt einhalb. Dies liegt an dem von uns vergebenen Datentyp (bei beiden Variablen int, Ganzzahl). Wenn zwei Ganzzahlen durcheinander geteilt werden wird das Ergebniss von Processing immer auf die nächste Ganzzahl abgerundet – in unserem Fall 2. Um dieses Problem zu umgehen muss mindestens eine der beiden Variablen eine Gleitkommazahl (float) sein:
float a = 5; float b = 2; // Ausgabe: 2.5 println (a / b);
int zahl = 94; // Ausgabe: 94 println (zahl); // erhöht den Wert um 1 zahl++; // Ausgabe: 95 println (zahl);
Operatoren sind Bestandteile die meist zur Formulierung von Fragen benönigt werden. Beispielsweise ob die Frucht ein Apfel ist und dieser die Farbe Grün besitzt.
== gleich!= ungleich> größer als>= größer gleich als< kleiner als⇐ kleiner gleich alsNach unserem Wissen müsste diese Abfrage folgende Struktur haben:
String fruchtSorte = "apfel"; String fruchtFarbe = "gruen"; if (fruchtSorte == "apfel") { if (fruchtFarbe == "gruen") { println ("greif zu!"); } }
Alle im Weiteren aufgeführten Elemente dienen zur Formulierung und Verknüpfung von Abfragen. Grundsetzlich muss das Resultat immer wahr oder falsch sein.
String fruchtSorte = "apfel"; String fruchtFarbe = "gruen"; if (fruchtSorte == "apfel" && fruchtFarbe == "gruen") { println ("greif zu!"); }
Nun wurden beide Fragen innerhalb einer einzigen if-Abfrage gestellt. Beide müssen zutreffen damit “greif zu!” in der Konsole erscheint. Im nächsten Schritt lassen wir neben grünen Äpfeln alle roten Früchte zu:
String fruchtSorte = "apfel"; String fruchtFarbe = "gruen"; if (fruchtSorte == "apfel" && fruchtFarbe == "gruen" || fruchtFarbe == "rot") { println ("greif zu!"); }
Um visuelle Komplexität zu generieren muss eine Vielzahl von Zeichenbefehlen aufgerufen werden. Viele Formen oder Formzusammenschlüsse weisen Ähnlichkeiten auf und unterscheiden sich im Quelltext nur durch unterschiedliche Angaben von Parametern (z.B. x1, y1, x2, …) die den Zeichenbefehlen folgen.
Im unteren Beispiel werden fünf Linien gleichmäßig mit einem Abstand von jeweils 20px auf der y-Achse verteilt.
void draw () { background (255); line (0, 0, 320, 0); line (0, 20, 320, 20); line (0, 40, 320, 40); line (0, 60, 320, 60); line (0, 80, 320, 80); }
Die x-Position der Anfangs- und Endpunkte bleibt bei allen fünf Aufrufen bei 0px bzw. 320px. Nur die y-Position erhöht sich von line() zu line() Aufruf. Im Folgenden lernen wir nun eine Schreibform die uns Arbeit erspart und uns die Möglichkeit gibt die Anzahl der Linien zu Steuern. Bisher haben wir in zwei Fällen von sogenannten “Blöcken” gesprochen. Blöcke die mittels geschweifter Klammern ( {…} ) einen besonderen Bereich markieren. Im Fall von draw() wird ein Einzelbild erstellt – bei if und else entscheidet eine Bedingung über die Ausführung des Programmabschnittes.
Nun lernen wir eine neue Art von Block kennen, die einen Bereich beschreibt der mehrfach ausgeführt werden kann. Das resultierende Konstrukt (Kontrollstruktur) wird als “Schleife” bezeichnet, von denen es mehrere Formen gibt.
Die einfachste Form einer Schleife ist die sogenannte while-Schleife. Formuliert wird sie durch die Angabe einer Laufbedinung und den geschweiften Klammern { }. Eine Laufbedinung muss eine Frage stellen, die klar mit Ja (true) oder Nein (false) beantwortbar ist. Hat eine Variable einen bestimmten Wert; liegt ein Wert unter/über einer Grenze; Ist die Maustaste gedrückt?. Wenn aus dieser Bedingung ein true resultiert wird der Code innerhalb des Schleifenkörpers (zwischen den geschweiften Klammern) ausgeführt. Kommt es jedoch zu einem false – wird die gesamte Schleife übersprungen und das Programm läuft weiter.
Im Falle einer Bedingung die immer mit Ja (true) beantwortet wird erhält man eine Endlosschleife. In dieser speziellen Version hängt das Programm in der while-Schleife und es werden beispielsweise keine weiteren Einzelbilder gezeichnet, wenn die while-Schleife im draw-Block platziert ist.
while (Bedingung) {
// repetiver Prozess
}
Formuliert wird die while-Schleife durch den Begiff while, gefolgt von der Bedingung in runden Klammern. Nachdem die Klammernkombination geschlossen wird beginnt die Defintion des Schleifenkörpers. Wie jeder weitere Block, z.B. setup() oder draw() wird dieser mit geschweiften Klammern umfasst. Wenn die Bedingung ein false als Resultat besitzt, läuft das Programm unterhalb des Schleifenkörpers, nach der zweiten geschweiften Klammer, weiter.
void draw () { background (255); float y=0; while (y <= 100) { line (0, y, 320, y); y = y + 20; } }
Unsere erste while-Schleife liefert das oberhalb eingeführte Muster aus vier horizontalen Linien. Bevor die Schleife mit dem Begriff while ins Leben gerufen wird, legen wir die Variable y mit dem Wert 0 an. In ihr soll die y-Position der Linien gespeichert werden.
Bevor der Schleifenkörper abgearbeitet wird, stellt Processing die in der Bedinung formulierte Frage:“Ist y kleiner-gleich 100?”. Da y zu Beginn den Wert 0 besitzt springt Processing in den Schleifenkörper und zeichnet eine Linie an der Position x1=0, y1=0, x2=320 und y2=0. Damit liegt diese direkt an der oberen Bildschirmkante. Die zweite Zeile im Schleifenkörper erhöht den Wert von y um 20. Da die Bedingung der while-Schleife positiv war versucht Processing nun ein zweites Mal die Schleife auszuführen. Der aktuelle Wert von y ist nun 20 – eine weitere Linie wird im Sketchfenster 20 Pixel unter der Ersten gezeichnet.
Processing schafft es genau fünf Linien untereinander zu setzen. Dann hat y einen Wert von 120. Die Frage ob es “unter, bzw. gleich 100 ist” gibt false zurück. Dieses Beispiel zeigt die Struktur und Funktion von while-Schleifen. Da die Anzahl der Durchläufe relativ gering ist und innerhalb des Schleifenkörpers simple Prozesse ablaufen, ist der große Vorteil von Schleifen nicht unmittelbar kenntlich. Trifft jedoch einer der beiden Punkte zu, wird dieses Konstrukt unverzichtbar.
void draw () { background (255); float y=0; while (y <= 100) { line (0, y, 320, y); y = y + 2; } }
Beide Beispiele unterscheide sich nur in der letzte Zeile des Blocks der while-Schleife. In der ersten Version erhöhen wir den Wert von y um 20, im zweiten Fall um 2. Das Resultat von Nummer zwei ist ein viel dichteres Muster aus Linien da wir 52 statt 5 Durchläufe der Schleife zählen.
Als zweite Form einer Schleife lernen wir nun die for-Schleife kennen. Ebenso wie die while Version dient sie dazu einen Codeblock als repitiven Prozess zu formulieren. Der Kontrollmechanismus der while-Schleife bestand aus einer Frage die mit true zum Ausführen der Schleife führte. Bei false wurde die Schleife abgebrochen bzw. gar nicht erst ausgeführt. Im Beispiel nutzten wir die Variable y um die Anzahl der Durchgänge zu regulieren und einen Abbruch nach fünf gezeichneten Linien zu erzwingen. Die for-Schleife ist neben der Abbruchbedingung mit zwei weiteren Bestandteilen ausgestattet, welche das Anlegen einer Variable zur Kontrolle (y im while Beispiel) überflüssig macht. Diesen Typ von Variable, der sich auf die Anzahl der Durchläufe auswirkt, nennt man “Zählvariable”. Beim Schleifenaufruf, der Initialisierung, wird diese angelegt und nach jedem Durchgang aktualisiert.
Neben der englischen Bezeichnung ist die for-Schleife unter dem Namen “Zählschleife” bekannt. Ausschlaggebend dafür ist die Zählvariable, welche diese Schleifenform von der while-Schleife unterscheidet. Aufgerufen wird die for-Schleife in Processing über die folgende Struktur:
for (Initialisirung; Test; Aktualisierung) {
// repetiver Prozess
}
Der Begriff for leitet die Schleife ein, gefolgt von dem in runden Klammern umfassten “Initialisierung-Test-Aktualisierung” Block. Zur Formulierung des Schleifenblocks folgen nach der geschlossenen runden Klammer ein Paar geschweifte Klammern { }. Alles innerhalb dieses Blocks wird als Schleifenkörper bezeichnet und bei jedem Durchlauf von Processing abgearbeitet.
Wie bei der while-Schleife kann es zu einer Endlosschleifen kommen. Grundlage dafür sind Fehler beim Verfassen der “Initialisierung-Test-Aktualisierung”-Kombination. Beispielsweise wenn die Zählvariable bei 0 startet und nach jedem Durchlauf um 1 erhöht wird, wobei der Test die Schleife nur abbricht, wenn der Wert der Zählvariable unter 0 kommt (was nie der Fall ist).
void draw () { background (255); for (int i=0; i < 5; i = i + 1) { line (0, i * 20, 320, i * 20); } }
Dies ist die mit einer for-Schleife vereinfachte Version des oberen Beispiels. Dabei ist die Zeilenanzahl zwar nur auf drei gesunken – es steht uns aber die Option offen den Teil i < 5 durch i < 50 zu ersetzen. Unsere Linie würde damit 50 mal abgebildet werden.
void draw () { background (255); for (int i=0; i < mouseY; i++) { // weise der Linienfarbe einen Farbwert // mittels der Zählvariable "i" zu stroke (i * 2, i, 0); // zeichne eine horizontale Linie // verwende den Wert der // Zählvariable als y-Position line (0, i, width, i); } }
Wir haben mit dem oberen Beispiel unseren ersten dynamischen Farbverlauf erstellt. Die Schleife beginnt bei 0px auf der y-Achse und zeichnet solange eine Horizontale bis sie unsere Maus erreicht. Dabei wird von Linie zu Linie der Farbwert leicht variiert – ein Verlauf entsteht.
size(300, 300); background (255); // aktiviere Kantenglättung smooth (); // deaktiviere outlines noStroke (); // führe die Schleife aus bis die Zählvariable "diameter" // einen Wert erreicht der kleiner als 0 ist. Verringere dabei // ihren Wert nach jedem Durchgang um 12. for (int diameter=255; diameter > 0; diameter = diameter - 12) { // setze die Füllfarbe neu fill (diameter); // zeichne den Kreis mit variablem Durchmesser ellipse (width / 2, height / 2, diameter, diameter); }
void draw () { // fülle die gesamte Zeichenfläche mit weiß background (255); // deaktiviere Umrandungen noStroke (); // erstelle zwei Variablen zur späteren // Ablage der Positionen float x; float y; // Schleife für die Matrix-Zeilen for (int i=0; i < 5; i = i + 1) { // Schleife für die Matrix-Spalten for (int j=0; j < 5; j = j + 1) { // DIESER TEIL WIRD FÜR JEDEN // KREIS AUSGEFÜHRT! // Berechnung der Position x = i * 20; y = j * 20; // bestimme die Füllfarbe und zeichne // anschließend die Ellipse fill (i * 50, 0, j * 50); ellipse (x, y, 27, 27); } } }
Bei der Verschachtlung von Zählschleifen muss nur auf die Namen der Zählvariablen geachtet werden. Doppelbelegungen sind hierbei nicht zulässig. Deshalb verwenden wir in der zweiten for-Schleife den Variablennamen j (i, j, k haben sich als Standard etabliert).