Letztes Update: 17.10.2017
Behandelte Befehle: int, float, byte, short, long, double, boolean, char, mouseX, mouseY, pmouseX, pmouseY, width, height, frameRate, frameCount, keyPressed, key, keyCode, mousePressed, mouseButton, displayHeight, displayWidth

Lernziele

  • Verschiedene Datentypen (Zahl, Text etc.) in Variablen speichern
  • Zuweisungen und komplexe Rechenausdrücke auflösen
  • Variablen zur Animation verwenden
  • Operatoren richtig anwenden
  • Wo sinnvoll: Systemvariablen verwenden

4.1 Grundlagen

Variablen sind Container für Informationen, zum Beispiel für eine Zahl oder für einen Text. Der Inhalt einer Variable kann sich jederzeit ändern, ist also variabel.

Video: Einführung in Variablen (8:23)

Deklarantion, Zuweisung, Verwendung

Variablen können Daten speichern. Um dies sinnvoll zu tun, benötigen wir für jede Variable:

  • einen Typ, der bestimmt, was Sie in der Variable speichern können, z.B. eine ganze Zahl, eine Kommazahl oder einen Buchstaben.
  • einen Namen, damit Sie später darauf zurückgreifen können und verschiedene Variablen auseinander halten können.

Deklaration

Um eine Variable zu verwenden, müssen Sie sie zunächst die Variable erstellen. Das nennt man deklarieren. Hier deklarieren wir eine Variable mit Namen foo vom Typ int (steht für integer, engl. für "ganze Zahl"):

int foo; // Deklaration

Für Kommazahlen oder genauer Gleitkommazahlen (engl. floating point numbers), verwenden wir den Typ float:

float krumm; // Deklaration

Allgemein sieht eine Deklaration wie folgt aus:

TYP NAME;

Die Deklaration reserviert Platz im Speicher, um den Wert einer Variablen zu abzulegen. Das ist auch einer der Gründe, warum ein Typ angegeben wird: unterschiedliche Typen verbrauchen unterschiedlichen Speicherplatz.

Variablenamen immer klein schreiben!

Zuweisung

Im nächsten Schritt legen Sie den inhalt der Variable fest, das nennt man Zuweisung. Dazu verwendet man das Gleichheitszeichen. Wichtig: Es handelt sich nicht um die Gleichheit aus der Mathematik! Bei der Zuweisung steht links vom Gleichheitszeichen immer ein Variablenname und rechts steht z.B. eine Zahl oder ein mathematischer Ausdruck wie 3 + 2. Die rechte Seite wird ausgewertet und das Ergebnis in die Variable hineingeschrieben.

foo = 5; // einfache Zuweisung

Gleitkommazahlen werden im englischsprachigen Raum mit Punkt geschrieben:

krumm = 9.3;

Auf der rechten Seite kann ein beliebig komplexer arithmetischer Ausdruck stehen.

foo = 5 + 2 * (10 - 5);

Dieser wird zunächst gemäß der bekannten Regeln (Punkt vor Strich, Klammerung) ausgewertet.

foo = 15

Erst wenn die rechte Seite fertig ausgewertet ist, wird der Variablen foo der Wert, hier also 15, zugewiesen.

Eine Zuweisung mit Gleichheitszeichen bedeutet nicht mathematische Gleichheit. Stattdessen ist folgender Prozess zu beachten: Zuerst die rechte Seite auswerten, erst dann das Result in die Variable (linke Seite) schreiben.

Verwendung

Schließlich kann man die Variabe verwenden. Hier wird der Variablenname durch den Inhalt der Variable ersetzt, das nennt man auch Substitution.

println(foo); // Verwendung im Befehl println
rect(foo, 40, 30, 30); // Verwendung im Befehl rect

Stellen Sie sich vor, dass Processing vor der Verarbeitung einer Zeile zunächst alle Variablen durch den jeweils aktuellen Wert der Variable ersetzt.

In unserem Bespiele hat die Variable foo den Wert 5. Aus der Zeile

rect(foo, 40, 30, 30);

macht Processing also zunächst

rect(5, 40, 30, 30);

Jetzt wird die Zeile "normal" ausgewertet.

Zuweisung mit Variablen

Variablen können auch auf der rechten Seite von Zuweisungen stehen:

boo = foo + 10;

Bevor die Addition (oder andere Operationen) ausgewertet wird, werden alle Variablen auf der rechten Seite ersetzt (substituiert) durch ihre jeweils aktuellen Werte.

boo = 5 + 10;

Im nächsten Schritt wird der arithmetische Ausdruck auf der rechten Seite ausgewertet:

boo = 15;

Jetzt kann die Zuweisung erfolgen: boo erhält den Wert 15.

Variablenwert erhöhen

Wie Sie später noch sehen werden, kommt es sehr häufig vor, dass Sie den Wert einer Variablen um einen Betrag erhöhen wollen. Zum Beispiel: Variable x speichert eine x-Koordinate und hat den Wert 10, diesen möchten Sie um 1 erhöhen. Zunächst deklarieren wir die Variable:

int x;
x = 10;

Häufiger Fehler: Sie schreiben einfach

x + 1; // falsch

In diesem Code wird aber der Wert der Variable x nicht verändert. Stattdessen müssen Sie schreiben:

x = x + 1; // richtig

Hier passiert folgendes. Im ersten Schritt wird der Wert von x eingesetzt:

x = 10 + 1;

Im nächsten Schritt wird die Addition aufgelöst:

x = 11;

Erst jetzt wird das Gleichheitszeichen, also die Zuweisung, ausgeführt, und x erhält den neuen Wert 11.

Deklaration und Zuweisung auf einer Zeile

Sie können Deklaration und Zuweisung auch verkürzt auf eine Zeile schreiben. Beachten Sie dennoch, dass hier zwei Dinge passieren:

int bla = 50; // zwei auf einen Streich
float blubb = 1.5;

Die allererste Zuweisung bei einer Variable nennt man auch die Initialisierung der Variable.

Wo steht die Deklaration im Code?

Wir beschäftigen uns hier mit sogenannten globalen Variablen. Alle diese Variablen sollten im aktiven Modus immer ganz oben im Code stehen.

// Alle Variablen ganz oben,
// also vor setup und draw
int x = 0;
int y = 0;

void setup() {}

void draw() {
   ellipse(x, y, 20, 20);
   x++;
   y++;
}

Welche Namen sind erlaubt?

Wenn Sie eigene Variablen definieren, müssen Sie ein paar Regeln beachten, harte und weiche. Wichtig ist zunächst, dass Groß- und Kleinschreibung unterschieden wird, d.h. dass meineVariable und meinevariable zwei unterschiedliche Variablen sind, die auch gern im selben Code ko-existieren dürfen.

Verwenden Sie im Code keine Umlaute (ü, ö, ä) und kein scharfes S (ß). Verwenden Sie keine Akzente aus anderen Sprachen wie Französisch oder Spanisch. Schreiben Sie also zaehler statt zähler und fussPosition statt fußPosition.

Harte Regeln (Syntax)

Die Verletzung einer harten Regel führt zu einer Fehlermeldung, d.h. diese Regeln sind Teil der Syntax von Processing bzw. Java:

Variablennamen...

  • dürfen nicht mit einem Schlüsselwort der Sprache Java identisch sein, z.B. dürfen Sie nicht int oder float als Variablenname verwenden
  • bestehen aus Ziffern (0-9), Buchstaben (a-z, A-Z) und zwei weiteren Zeichen (Unterstrich und $), die aber besser nicht verwendet werden
  • dürfen nicht mit einer Ziffer beginnen
  • dürfen keine Leerzeichen enthalten und auch keinen Bindestrich (würde als Minus interpretiert werden)

Beispiele:

// Syntaktisch falsch:

007
meine variable
float
meine-variable

// Syntaktisch korrekt:

take5
this_is_for_you
iam007
MEINEVARIABLE

Weiche Regeln (Konventionen)

Neben den harten Regeln gibt es einige Konventionen, die Sie einhalten sollten, um nicht direkt als Programmieranfänger ertappt zu werden.

Variablennamen...

  • beginnen immer mit Kleinbuchstaben
  • folgen der CamelBack-Regelung, wenn Einzelwörter verbunden sind (z.B. meineNeueVariable)
  • sind möglichst aussagekräftig (z.B. hintergrundFarbe statt hg)

Beispiele:

// verletzt Konventionen (obwohl syntaktisch korrekt):

MeineVariable // groß geschrieben!
MEINEVARIABLE // GROSS GESCHRIEBEN!
meine_variable
mv

// entspricht Konventionen:

take5
thisIsForYou
iAm007

Übungsaufgaben

(a) Durchschnitt

Erzeugen Sie drei Variablen a, b und c vom Typ float. Setzen Sie a und b auf beliebige Werte, z.B. auf 1 und 2. In Variable c soll der Durchschnitt von a und b berechnet werden.

Geben Sie c mit println() auf der Konsole aus.

(b) Variablen für Position

Zeichnen Sie ein 30x30-Quadrat an der Position (50, 50). Ersetzen Sie die Position durch zwei Variablen x und y. Setzen Sie die Variablen auf verschiedene Werte und testen Sie, ob Ihr Quadrat an der richtigen Stelle gezeichnet wird.

(c) Variablen für Position 2

Erstellen Sie zwei Variablen x und y und belegen Sie sie mit jeweils 10. Zeichnen Sie ein 30x30-Quadrat an ebendiese Position. Zeichnen Sie ein zweites Quadrat an eine Position, die 50 Pixel weiter rechts und 50 Pixel weiter unten liegt. Benutzen Sie für das zweite Quadrat auch die beiden Variablen!

(d) Smiley

Erstellen Sie zwei Variablen x und y und belegen Sie sie mit jeweils 50. Zeichnen Sie einen Smiley aus entsprechenden Grundformen. Verwenden Sie für die Positionen der Formen immer die Variablen x und y zusammen mit Addition/Subtraktion. Ändern Sie aber nie die Werte von x und y.

Ersetzen Sie jetzt die Variablen x und y gegen mouseX und mouseY und überführen Sie das Programm in den aktiven Modus (mit setup und draw). Der Smiley sollte der Maus folgen.

(Interaktives Feld - erst anklicken.)

(e) Pythagoras

Erstellen Sie zwei Variablen x und y, jeweils vom Typ float und belegen Sie sie mit beliebigen Werten (z.B. x = 20, y = 30). Ihr Programm soll eine Linie zwischen (x, y) und der aktuellen Mausposition zeichnen und auf der Konsole ständig die Distanz (in Pixeln) zwischen (x, y) und der Maus ausgeben.

Die Funktion sqrt() gibt Ihnen zu einer beliebigen Zahl die Wurzel zurück. Mit der Funktion pow() können Sie Potenzen bilden. Zum Beispiel gibt pow(3, 2) das Ergebnis von "3 hoch 2" zurück.

Zusammenfassung

  • Variable als Container (Speicher): Eine Variable speichert etwas, z.B. einen Wert zur späteren Verwendung. Was genau sie speichern kann, sagt ihr Datentyp (kurz: Typ).
  • Erzeugen: Eine neue Variable erzeugt man mit TYP NAME;
  • Zuweisung: Hat man eine Variable foo, so kann man mit foo = 5 den Wert 5 dort hinein tun.
  • Verwendung: Steht eine Variable wie foo im Code - z.B. in dem Befehl rect(foo, foo, 10, 10) - so wird "foo" durch den aktuellen Wert von foo ersetzt (Substitution).

4.2 Datentypen

Der (Daten-)Typ einer Variable bestimmt, welche Inhalte die Variable speichern kann.

Die meisten der folgenden Datentypen werden primitive Datentypen genannt (Ausnahme: String). Die primitiven Typen erkennt man daran, dass sie klein geschrieben sind (int, float, ...), wohingegen nicht-primitive Datentypen (hier: String) groß geschrieben werden. Den genauen Unterschied lernen wir erst später kennen. Für Sie liegt der Unterschied lediglich in der Groß-/Kleinschreibung.

Alles über primitive Variablen von Oracle (englisch): Primitive Data Types

Zahlen

In Programmiersprachen unterscheidet man zwischen ganzen Zahlen und Kommazahlen (auch: Gleitkommazahlen).

Ganze Zahlen

Ganze Zahlen sind Zahlen ohne Nachkommestellen, im Englischen integer genannt. Es gibt mehrere Typen, die ganze Zahlen speichern, je nachdem wie groß/klein die Zahlen sind, die man speichern möchte. In der Regel verwendet man int .

TypBeschreibungWertebereich
byte sehr kleine Zahl (8 Bit)-128 bis 127
short kleine Zahl (16 Bit)-32768 bis 32767
int große Zahl (32 Bit)-2 147 483 648 bis 2 147 483 647
long noch größere Zahl (64 Bit)ca. -9 * 10^18 bis 9 * 10^18

Beispiel:

int foo;
foo = 50;
rect(boo, boo, 20, 20);

Bei Zahlen muss Processing manchmal "raten", ob Sie eine ganze Zahl meinen oder eine Gleitkommazahl. Prinzipiell gilt: Wenn Sie eine Zahl ohne Komma (bzw. Punkt) schreiben, interpretiert Processing die Zahl als ganze Zahl vom Typ int.

Gleitkommazahlen

Gleitkommazahlen sind Zahlen mit Nachkommastellen (auch Dezimalzahlen genannt). In Processing verwenden Sie meistens float , später in Java werden Sie meistens double verwenden.

Schreiben Sie Gleitkommazahlen - so wie in England und USA üblich - mit einem Punkt. Also 1.5 statt 1,5.
TypBeschreibungWertebereich
float Zahl mit Nachkommastellen (32 Bit)z.B. -0.5, 0, 109.333
double Zahl mit mehr Nachkommastellen (64 Bit)wie oben

Beispiel:

float x;
x = 75.0 / 2;
ellipse(x, 50, 30, 30);

Auch hier muss Processing oft "raten", ob Sie eine ganze oder eine Gleitkommazahl meinen. Prinzipiell gilt: Wenn Sie eine Zahl mit Nachkommastellen schreiben, interpretiert Processing die Zahl als Gleitkommazahl vom Typ float.

Wenn Sie z.B. die Zahl 5 als Gleitkommazahl interpretiert haben möchten, müssen Sie 5.0 schreiben.

Umwandlung zwischen int und float

Es gibt Situationen, da wollen Sie aus einer ganzen Zahl (5) eine Gleitkommazahl (5.0) machen und umgekehrt. Processing bietet dazu zwei Befehle int() und float() an.

Zum Beispiel liefert der Befehl random() eine float-Zahl zurück. Wenn Sie aber lieber eine int-Zahl hätten, schreiben Sie:

int x = int(random(100));

Dabei wird der ganzzahlige Anteil genommen, d.h. bei 3.9 wird das Ergebnis von int(3.9) eine 3 sein. Es wird also nicht gerundet.

Umgekehrt können Sie mit float(5) aus der 5 eine Gleitkommazahl (5.0) machen. Das ist natürlich hauptsächlich dann sinnvoll, wenn Sie eine int-Variable z haben, die Sie mit float(z) in einen Float-Wert umwandeln. Dies kann z.B. bei Division sinnvoll bzw. notwendig sein (siehe Abschnitt zu ganzzahliger Division).

Wahrheitswerte, Buchstaben, Text

Wahrheitswerte (boolean)

Wahrheitswerte lernen Sie in Zusammenhang mit If-Anweisungen kennen. Ein Wahrheitswert ist entweder wahr (true) oder falsch (false). Es gibt also zwei mögliche Werte für eine solche Variable. Der Typ heißt boolean in Hommage an den englischen Logiker George Boole (1815-1864). Dabei ist boolean ein Adjektiv, also wie im Deutschen "eine boole'sche Variable".

TypBeschreibungWertebereich
boolean Wahrheitswert true und false

Beispiele:

boolean iLikeJava;
boolean iLikeC;
iLikeJava = true;
iLikeC = false;
println("Java: " + iLikeJava);
println("C: " + iLikeC);

Zeichen (char)

In Processing gibt es einen Datentyp für einzelne Zeichen. Ein Zeichen ist z.B. ein Buchstabe wie a, A, m, Z oder auch ein Sonderzeichen wie #, ! oder /. Im Englischen heißt Zeichen character, daher heißt der Datentyp char .

TypBeschreibungWertebereich
char einzelnes Zeichen (z.B. Buchstabe)z.B. 'a' oder '%'

Sie können ein einzelnes Zeichen in einer Variable abspeichern. Das Zeichen wird immer mit einfachen Anführungszeichen umgeben:

char c1 = 'a';
char c2 = '5';
char c3 = '%';
println(c1 + c2 + c3);

Intern wird ein Zeichen als ganze Zahl repräsentiert. Diese Zahl entspricht einer standardisierten Tabelle. Für die handelsüblichen Buchstaben ist das zum Beispiel der ASCII-Code (ohne Umlaute). ASCII steht für "American Standard Code for Information Interchange" und ist ganz einfach eine Tabelle, wo für jedem Buchstaben/Zeichen eine Zahl zugeordnet wird. Heutzutage verwendet man das Unicode-System, welches für fast alle Sprachen der Welt entsprechende Buchstaben-/Zeicheneinträge hat. Eine konkrete Enkodierung von Unicode ist z.B. UTF-8.

Zeichenketten (String)

String ist englisch für "Schnur" oder "Kette". Das deutet darauf hin, dass ein String eine Aneinanderreihung von Zeichen (engl. character) ist. Man sagt deshalb auch Zeichenkette. Der Typ String wird im Gegensatz zu int, float, char ... groß geschrieben. Warum das so ist, erfahren Sie, sobald Sie Klassen und Objekte kennenlernen.

In String-Variablen können Texte ablegt werden. Der Text wird mit doppelten Anführungszeichen gekennzeichnet:

String foo = "Dies ist ein Text!";

Sie haben Strings bereits im Zusammenhang mit println() gesehen:

println("hallo");
hallo

Genauso könnten Sie schreiben:

String message = "hallo";
println(message);

Sie können Strings zusammenkleben, dies nennt man String-Konkatenation und funktioniert mit dem normalen Plus-Zeichen:

String message = "hallo";
println(message + " bob");
hallo bob

Das Plus-Zeichen bekommt also im Zusammenhang mit Strings eine andere Bedeutung. Wenn Sie eine Zahl mit einem String "addieren", wird die Zahl in einen String verwandelt:

String message = "high" + 5;
println(message);
high5
    

Sie können Strings auch direkt in das print schreiben:

println("high" + 5);

Übungsaufgaben

(a) Mausposition ausgeben

Schreiben Sie ein Programm, das in jedem draw-Zyklus die aktuelle Mausposition in folgender Weise ausgibt (eine Zeile pro Zyklus):

x=48 y=47
x=48 y=52
x=48 y=56
x=48 y=60
x=52 y=65
x=52 y=65
x=55 y=70
Tipp
Verwenden Sie println zusammen mit mouseX und mouseY (ein einziger Befehl). Lesen Sie den Abschnitt oben über "Zeichenketten".

(b) Halbe Pixel?

Kopieren Sie folgendes Programm

float x = 0;

void draw() {
  background(255);
  ellipse(x, 50, 30, 30);
}

Ergänzen Sie den Code, so dass x bei jedem Mausklick um 0.5 erhöht wird.

Wie verhält sich der Kreis?

Tipp
Verwenden Sie mousePressed() und schauen Sie in Kap. 3 nach, wenn Sie vergessen haben, wie das funktioniert.

(c) Zeichenverkettung

Kopieren Sie folgendes Programm

String message = "la";

void setup() {
  println(message);
}

void draw() {
}

Ergänzen Sie den Code, so dass bei bei jedem Mausklick genau eine neue Zeile auf die Konsole geschrieben wird, so dass die Konsole nach 3 Klicks wie folgt aussieht:

la
la la
la la la
la la la la

(d) Schreibmaschine *

Schreiben Sie ein Programm, dass die Eingabe eines Strings über das Grafikfenster erlaubt.

Wenn der Benutzer schreibt, soll ein String "eingabe" mit den Buchstaben gefüllt werden. Gleichzeitig sieht man im Grafikfenster das Getippte. Wenn der Benutzer ENTER drückt, wird der String auf der Konsole ausgegeben.

Schauen Sie nochmal in Abschnitt 3.4 nach, wie man Text im Grafikfenster darstellt.

(Interaktives Feld. Erst anklicken, dann interagieren.)

(e) Schwarzweiß

Der Hintergrund soll schwarz eingefärbt werden, wenn Sie eine Tastatur-Taste drücken, und weiß, wenn Sie eine Maustaste drücken.

Verwenden Sie folgenden Basiscode und achten Sie darauf, dass das Rechteck nicht übermalt wird, wenn Sie den Hintergrund ändern. Verwenden Sie KEINE Zeichenbefehle (background, rect) in keyPressed() oder mousePressed().

void draw() {
  rectMode(CENTER);
  rect(50, 50, 40, 40);
}

Wichtig: Verwenden Sie eine Variable, die Sie in keyPressed bzw. mousePressed verändern (sonst funktioniert die Folge-Aufgabe nicht).

(Interaktives Feld - erst anklicken.)

(f) Schwarzweiß II

Ersetzen Sie im obigen Programm das Quadrat durch einen Kreis, der komplementär zum Hintergrund schwarz oder weiß ist. Verwenden Sie KEINE Zeichenbefehle (background, ellipse) in keyPressed() oder mousePressed().

(Interaktives Feld - erst anklicken.)

(g) Zufällige Position

Zeichnen Sie einen schwarzen Punkt. Bei jedem Mausklick soll der Punkt an eine zufällige Position springen.

(Interaktives Feld - anklicken.)

Tipp
Verwenden Sie Variablen vom Typ float für x und y. Lassen Sie den Code zum Zeichnen immer in draw und verwenden mousePressed lediglich, um Variablen zu ändern.

Zusammenfassung

Sie haben die folgenden primitiven Datentypen kennengelernt:

TypBeschreibungWertebereich
byte sehr kleine ganze Zahl (8 Bit)-128 bis 127
short kleine ganze Zahl (16 Bit)-32768 bis 32767
int große ganze Zahl (32 Bit)-2 147 483 648 bis 2 147 483 647
long noch größere ganze Zahl (64 Bit)ca. -9 * 10^18 bis 9 * 10^18
float Zahl mit Nachkommastellen (32 Bit)z.B. -0.5, 0, 109.333
double Zahl mit mehr Nachkommastellen (64 Bit)wie oben
boolean Wahrheitswert true und false
char einzelnes Zeichen (z.B. Buchstabe)z.B. 'a' oder '%'

Desweiteren haben Sie den Typ String kennengelernt, der es erlaubt, Zeichenketten (= Text) abzuspeichern.

Strings können mit dem Plus-Zeichen verbunden werden. Diese Operation nennt man String-Konkatenation.

4.3 Verarbeitung von Code

Code wird Zeile für Zeile verarbeitet, von oben nach unten. Processing hat zwei Bereiche für die Verarbeitung:

  • einen Bereich, wo die aktuelle Zeile gespeichert und verarbeitet wird
  • einen Bereich, wo alle aktuell gültigen Variablen abgelegt werden

Damit Sie verstehen, wie Processing funktioniert, schauen wir uns an, wie genau eine einzelne Zeile verarbeitet wird. Nach derzeitigem Stand können folgende Dinge in einer Zeile passieren:

  • Eine Variable wird deklariert
  • Einer Variablen wird ein Wert zugewiesen
  • Ein Befehl

Wird eine Variable deklariert, wird ein neuer "Behälter" im Variablenbereich für diese Variable angelegt. Im weiteren schauen wir uns die anderen zwei Fälle an.

Video: Variablen 2 - Verarbeitung (5:38)

Verarbeitung einer Variablenzuweisung

Allgemein sieht eine Variablenzuweisung immer so aus:

VARIABLE = AUSDRUCK;

Beispiel:

int x;
x = 10 + 5; // rechte Seite wird zuerst ausgewertet

Wichtig ist jetzt, dass Processing immer zuerst die rechte Seite auswertet und anschließend das Ergebnis in die Variable x schreibt.

Ist die Variable eine Zahl (int, float, double), so kann der Ausdruck die verschiedenen mathematischen Operatoren +, -, /, *, % und Klammern beinhalten.

int b;
b = 20 + 4 / 2 + (5 - 2);

Mathematische Ausdrücke werden nach den bekannten Regeln ausgewertet, d.h. * und / haben Priorität, geklammerte Teile ebenso. Im Beispiel ist b also 25.

Die rechte Seite einer Zuweisung kann auch Variablen enthalten. In diesem Fall werden bei der Auswertung als erstes alle Variablen durch Ihre Werte ersetzt (nennt man auch Substitution).

int q = 33;
int ergo;
ergo = 22 + q + 5; // zuerst wird q eingesetzt

Oft möchte man den Wert einer Variablen um einen bestimmten Betrag erhöhen oder erniedrigen. Das funktioniert wie folgt:

int foo = 100;
foo = foo + 10;
  

Warum muss das so aussehen? In dieser Zeile wird zunächst die Variable ersetzt:

foo = foo + 10;
  

Das sieht dann so aus:

  foo = 100 + 10;
  

Jetzt wird gerechnet:

  foo = 110;
  

Erst jetzt wird der Wert von foo auf 110 angepasst. Unser Ziel, den Wert um 10 zu erhöhen, ist erreicht.

Verarbeitung eines Befehls

Allgemein sieht eine Befehlszeile so aus

BEFEHL(PARAMETER-1, PARAMETER-2, ...);

Trifft Processing auf einen Befehl, so können auch dort Variablen auftauchen. Auch hier werden zuerst alle Variablenwerte eingesetzt, erst dann wird der Befehl ausgeführt.

Bespiele:

int x = 50;
int y = 100;
ellipse(x, y, 30, 30);
rect(x, y, 30, 20);
println("x ist " + x);

Globale Variablen und Animation

Sie wissen aus dem letzten Kapitel, dass im aktiven Modus zuerst setup() genau ein Mal ausgeführt wird und anschließend immer wieder draw() ausgeführt wird.

Es kommt eine neue wichtige Regel hinzu: Noch bevor setup() ausgeführt wird, werden alle Codezeilen ausgeführt, in denen globale Variablen deklariert/initialisiert werden.

Beachten Sie die Ausführungsreihenfolge. Zuerst werden globalen Variablen initialisiert, dann wird setup() ausgeführt, dann wird draw() wiederholt ausgeführt.

Ein Beispiel, um die Ausführungsreihenfolge zu prüfen:

int foo = 0;

void setup() {
  println(foo);
  foo = foo + 1;
}

void draw() {
  println(foo);
  foo = foo + 100;
}
0
1
101
201
301

Animation

Wir können jetzt eine Variable verwenden, um etwas zu animieren, indem wir die Variable (z.B. die x-Koordinate einer Form) immer wieder verändern:

int x = 0;

void draw() {
  ellipse(x, 50, 20, 20);
  x = x + 1;
}

Wenn wir keine "Schlieren" sehen möchten, müssen wir den Hintergrund in jedem Durchlauf von draw() erneuern.

int x = 0;

void draw() {
  background(200);
  ellipse(x, 50, 20, 20);
  x = x + 1;
}

Ausführungsreihenfolge: Ein Beispiel

Wir haben oben gesagt, dass der Programmteil mit den Variablen ganz oben als erstes ausgeführt wird. Das sollte man beachten, wenn man will, dass Variablen immer wieder neu gesetzt werden. Nehmen wir an, wir möchten immer wieder einen zufällig gewählten Punkt malen, so dass sich langsam der Bildschirm füllt.

// Wird als erstes ausgeführt:

float x = random(100);
float y = random(100);

// Wird als zweites ausgeführt:

void setup() {
  frameRate(10); // 10x draw() pro Sekunde
  strokeWeight(5); // Punktgröße = 5 Pixel
}

// Wird als 3., 4., 5. etc. ausgeführt:

void draw() {
  point(x, y); // male Punkt
}

Warum sehen wir nur einen Punkt? Ganz einfach: wegen der Ausführungsreihenfolge. Der Teil mit den globalen Variablen x und y wird als erstes ausgeführt. Hier werden zwei Zufallszahlen bestimmt. Als nächstes wird 1x setup() ausgeführt. Anschließend wird immer wieder draw() ausgeführt. Nur die Werte für x und y ändern sich nie mehr. Also wird immer wieder der gleiche Punkt gemalt.

Wir müssen also das Beziehen der Zufallszahlen mit random() in das draw() ziehen:

float x;
float y;

void setup() {
  frameRate(10);
  strokeWeight(5);
}

void draw() {
  x = random(100);
  y = random(100);
  point(x, y);
}

Anwendungsbeispiele

Zwei typische Anwendungen von Variablen sind

Eine zentrale Stelle einzurichten, wo häufig verwendete Werte abgelegt werden, zum Beispiel der Durchmesser von mehreren Kreisen, die Geschwindigkeit oder Farbe von verschiedenen Objekten. Der Vorteil liegt darin, dass dieser Wert immer nur an einer Stelle geändert werden muss.

Einen Wert während des Programmlaufs ändern zu können. Der Name Variable kommt natürlich von variabel. Bei einer Animation ändern sich ständig z.B. den Wert der x-Koordinate und können das nur tun, indem Sie die x-Koordinate in einer Variablen speichern und ständig anpassen (z.B. 1 addieren).

Ein letztes Beispiel: Sie kennen aus dem Kapitel über Interaktion die Möglichkeit mit keyPressed() auf Tastendruck zu reagieren.

Können Sie die Hintergrundfarbe in folgendem Beispiel per Tastendruck verstellen?

int x = 0;

void setup() {
  size(200, 200);
}

void draw() {
  background(255);
  rect(x, 100, 20, 20);
  x++;
}

void keyPressed() {
  background(0, 255, 0);
}

Im Beispiel sehen Sie nur ein kurzes Aufflackern der Farbe Grün. Warum? Weil das background(255) immer wieder den Bildschirm mit Weiß befüllt. Um das Grün nachhaltig zu etablieren, müssen Sie Variablen einführen:

int x = 0;
int rot = 255;
int gruen = 255;
int blau = 255;

void setup() {
  size(200, 200);
}

void draw() {
  background(rot, gruen, blau);
  rect(x, 100, 20, 20);
  x++;
}

void keyPressed() {
  rot = 0;
  gruen = 255;
  blau = 0;
}

Da jetzt die Variablen rot, gruen, blau umgesetzt werden, sobald eine Taste gedrückt wird, ändert sich das background()-Kommando in draw(). Es ergibt sich für alle folgenden draw()-Zyklen das Kommando background(0,255,0) - also grün.

Video: Anwendungsbeispiele (10:14)

Übungsaufgaben

(a) Ball nach rechts

Schreiben Sie ein Programm, bei dem sich ein Ball, der zu Beginn an Position (0, 50) steht, bei jedem Mausklick um 5 Pixel nach rechts bewegt.

(Interaktives Feld - erst anklicken.)

(b) Quadrat wächst/schrumpft

Schreiben Sie ein Programm, bei dem sich ein Quadrat, das in der Mitte steht, bei jedem Mausklick um 5 Pixel vergrößert, und bei jedem Tastenanschlag um 5 Pixel verkleinert.

(Interaktives Feld - erst anklicken.)

(c) Ball fliegt nach rechts

Schreiben Sie ein Programm, bei dem sich ein Ball, der zu Beginn an Position (0, 50) steht, nach rechts fliegt.

Im folgenden Feld sehen Sie, dass der Ball immer wieder von links eintritt. Das können Sie noch nicht realisieren - es sei denn, Sie kennen bereits If-Anweisungen, dann können Sie es gern probieren. (Alternativ lesen Sie unten den Abschnitt über den "Modulo-Operator".)

(Interaktives Feld - erst anklicken.)

4.4 Lokale Variablen

Bislang kennen Sie ausschließlich globale Variablen. Diese werden zu Beginn des Programms deklariert und können dann überall verwendet werden, z.B. in den Funktionen setup() und draw(), aber auch in keyPressed():

int x; // neue globale Variable

// Anfangsposition festlegen
void setup() {
  x = 0;
}

// Ball animieren
void draw() {
  background(255);
  ellipse(x, 50, 20, 20);
  x++;
}

// Ball zurücksetzen
void keyPressed() {
  x = 0;
}

Variable x nennt man eine globale Variable, weil überal, also global, im Programm verwendet werden kann. In unserem Beispiel wird x in drei verschiedenen Funktionen (setup, draw und keyPressed) verwendet.

Manchmal möchte man gar nicht, dass eine Variable überall verwendbar ist. Wie sich bei größeren Programmen herausstellt, wird der Code durch viele globale Variablen unübersichtlich. Daher gibt es lokale Variablen, die nur eine eingeschränkte Lebensdauer haben, d.h. sie sind nur in einem bestimmten Bereich gültig.

Den Bereich, innerhalb dessen eine Variable gültig ist, nennt man den Skopus der Variable (engl. scope).

Nehmen wir an, Sie möchten einen Ball animieren. Der Durchmesser des Balls soll wachsen, also nehmen wir einfach den x-Wert auch als Durchmesser. Wir möchten aber, dass der Durchmesser nicht bei 0, sondern bei 10 beginnt. Also nehmen wir x + 10 als Durchmesser:

float x = 0;

void draw() {
  ellipse(x, 50, x + 10, x + 10);
  x++;
}

Der Übersichtlichkeit willen möchten wir eine Variable für den Durchmesser einführen. Diese wird aber lediglich in draw() benötigt (nicht z.B. in setup). Daher definieren wir eine lokale Variable:

float x = 0;

void draw() {
  float durchmesser = x + 10; // Neue lokale Variable
  ellipse(x, 50, durchmesser, durchmesser);
  x++;
}

Eine lokale Variable ist nur im eigenen Code-Block gültig, angefangen von der Zeile, wo die Variable deklariert wird bis zur schließenden geschweiften Klammer des aktuellen Code-Blocks. Im oberen Beispiel hört die Variable durchmesser auf zu leben, sobald draw abgearbeitet ist.

Wollten Sie z.B. im folgenden Code auf die Variable durchmesser in der Funktion setup() zugreifen, dann bekommen Sie einen Fehler:

float x = 0;

void setup() {
  durchmesser = 10; // FEHLER! VARIABLE UNBEKANNT
}

void draw() {
  float durchmesser = x + 10; // Neue lokale Variable
  ellipse(x, 50, durchmesser, durchmesser);
  x++;
}

Wichtig ist, dass nur das Wörtchen float (oder int oder boolean ...) darüber entscheidet, ob Sie eine neue Variable anlegen oder eine alte Variable setzen. Wie in folgendem Beispiel, wo Sie den x-Startpunkt des Balls zufällig setzen wollen:

float x = 0;

void setup() {
  float x = random(100); // Startpunkt zufällig
}

void draw() {
  background(255);
  ellipse(x, 50, 20, 20);
  x++;
}

Ihr Ball startet immer bei Null! Warum? Weil in setup() eine neue Variable mit Namen x erzeugt wird. Diese neue Variable ist lokal in setup() und hat nichts mit dem globalen x zu tun. Das heißt, das globale x wird nicht verändert, bleibt also Null, und in draw() wird natürlich das globale x verwendet.

Übungsaufgaben

Sie können bei den folgenden Aufgaben lokale Variablen nutzen (müssen Sie aber nicht).

(a) Zwillinge

Programmieren Sie folgendes:

(Interaktives Feld - erst anklicken.)

(b) Mitte

Programmieren Sie folgendes:

(Interaktives Feld - erst anklicken.)

4.5 Operatoren

Operatoren sind Symbole, die ein oder zwei Operanden verbinden. Das Plus-Zeichen ist z.B. ein Operator. Die dazugehörig Operation heißt Addition. Hier stellen wir noch einige nützliche Operatoren vor.

Ganzzahlige Division und Modulo

Sie kennen das Symbol / als Operator für die "geteilt durch" Operation. Was Sie noch nicht wissen, ist, dass es zwei unterschiedliche Divisionen gibt: die ganzzahlige Division und die Gleitkommazahlen-Division. Beide Divisionen verwenden aber das gleiche Symbol /. Bei der ganzzahligen Division haben wir zwei Resultate: das Ergebnis der Division und den Rest (Modulo).

Ganzzahlige Division

Sehen wir uns das im Beispiel an. Hier haben wir eine ganzzahlige Division:

println(5 / 2);
2

Wie entscheidet Processing, ob eine ganzzahlige Division verwendet wird, oder die Gleitkomma-Division? Die Regel lautet: Wenn beide Operanden des Operators (hier: die 5 und die 2) vom Typ int sind, dann verwende die ganzzahlige Division.

Hier haben wir ein Beispiel für eine Gleitkomma-Division:

println(5.0 / 2);
2.5

Durch das .0 wird die fünf als Gleitkommazahl interpretiert. Das könnten wir natürlich auch so bewirken:

  println(float(5) / 2);
  

In diesem Fall ist das etwas umständlich, bei Variablen ist es aber manchmal notwendig:

int zahl = 5;
println(float(zahl) / 2);
  

Achten Sie darauf, dass bei komplexeren Rechnungen nicht immer offensichtlich ist, von welchem Typ die jeweiligen Operanden sind. Sich müssen sich genau überlegen, in welcher Reihenfolge gerechnet wird und welchen Typ die Operanden zu dem Zeitpunkt haben, wo der Divisions-Operator an der Reihe ist:

println(2.5 + 5 / 2 + 5.0);

Bekommen Sie hier eine 10.0 oder eine 9.5? Versuchen Sie sich zuerst klarzumachen, was herauskommt. Probieren Sie es dann in Processing aus.

Modulo-Operator

Wir haben gelernt, dass Processing eine ganzzahlige Division durchführt, wenn zwei Integer-Werte die Operanden sind. Das Resultat einer ganzzahligen Division hat zwei Bestandteile: den Ganzzahlquotient und den Rest.

5 / 2 ergibt 2 (Quotient) mit Rest 1
7 / 5 ergibt 1 (Quotient) mit Rest 2

Mathematisch ist das wie folgt definiert:

x / y ergibt Quotient q mit Rest r

x = q * y + r

Der Modulo-Operator % gibt Ihnen für eine ganzzahlige Division den Rest zurück. Hier sehen Sie ihn in Aktion:

println(5 % 2);
println(7 % 5);
1
2

Der Modulo-Operator erlaubt Ihnen zu testen, ob eine Zahl gerade oder ungerade ist, denn wenn man eine gerade Zahl durch 2 teilt, ist der Rest gleich Null:

int zahl = 7;

if (zahl % 2 == 0) {
  println("gerade");
} else {
  println("ungerade");
}

Ein Grund, warum der Modulo-Operator in der Informatik und insbesondere im Bereich Animation so interessant ist, ist seine Ring-Eigenschaft. Dazu sehen wir uns an, was passiert, wenn man sich den Rest von einer Zahlenreihe (0, 1, 2, 3, ...) mit einer festen Zahl (z.B. 3) ansieht:

0 % 3 = 0
1 % 3 = 1
2 % 3 = 2
3 % 3 = 0
4 % 3 = 1
5 % 3 = 2
6 % 3 = 0
7 % 3 = 1
8 % 3 = 2
...

Fällt Ihnen etwas auf? Richtig. Obwohl der erste Operand immer größer wird, bewegt sich das Ergebnis der Modulo-Operation immer im Bereich 0...2.

Allgemein bewegt sich bei einer Restoperation mit der Zahl N das Resultat immer im Bereich von 0 bis N-1.

Das ist sehr nützlich bei Animationen:

int x = 0;

void draw() {
  background(255);
  int x2 = x % 100;
  ellipse(x2, 50, 20, 20);
  x++;
}

Obwohl x immer größer wird, bleibt x2 im Bereich 0..99, so dass der Ball immer von links nach rechts im sichtbaren Bereich fliegt.

Interessante Operatoren

Mit den folgenden Operatoren können Sie stellenweise Ihren Code verkürzen oder eleganter formulieren.

Inkrement-Operator

Eine nützliche Kurzform für folgende Operation (x sei eine int-Variable)

x = x + 1;

ist die folgende Zeile

x++;

Das bedeutet, dass der Inhalt der Variable x um 1 erhöht wird. Das "++" nennt man einen Operator, und zwar den Inkrement-Operator.

Dekrement-Operator

Passend dazu gibt es einen Operator zum Runterzählen:

x--;

Dieser sog. Dekrement-Operator reduziert den Wert von x um 1.

Additions-Zuweisung

Was machen Sie, wenn Sie so eine Erhöhung haben:

x = x + 2;

Ganz einfach: Sie nutzen die Additions-Zuweisung:

x += 2;

Sie denken: Naja, soviel Schreibarbeit hab ich hier nicht gespart! Schauen Sie mal:

ziemlichLangerVariablennameMitExtras += 2;

Schon praktisch. Diese Art der Zuweisung gibt's auch in anderen Geschmacksrichtungen:

            x -= 3; // x = x - 3
            x *= 5; // x = x * 5
            x /= 2; // x = x / 2

Tipp: Wie merken Sie sich, ob es jetzt "+=" oder "=+" heißt? Überlegen Sie sich, dass "x =+ 2" von Processing gelesen würde wie "x = +2", d.h. das Plus-Zeichen wird der Zahl zugeordnet und x daher nicht um 2 erhöht, sondern auf 2 gesetzt.

Details zum Inkrementoperator

Ausführungsreihenfolge

Sie können den Inkrement-Operator nicht nur allein auf einer Zeile ausführen, sondern auch in Befehle oder Rechnungen einbetten.

Beispiel 1:

int foo = 5;
println(foo++);

Beispiel 2:

int boo = 10;
int goo = 100 + boo++;

Was passiert hier? Wir haben hier zwei Vorgänge, die wir unterscheiden müssen:

  • (a) Verwendung/Substitution der Variable, d.h. die Variable wird durch ihren aktuellen Wert ersetzt
  • (b) Inkrementierung, d.h. der Wert der Variable wird um eins erhöht.

Jetzt wird offenbar, dass es eine große Rolle spielt, ob erst (a) oder (b) durchgeführt wird! Wenn Sie Beispiel 1 ausführen, sehen Sie:

5

Der Wert der Variable foo ist aber 6. Das liegt daran, dass beim Einsetzen (a) der alte Wert 5 verwendet wurde. Im Anschluss wurde dann die Variable foo erst um eins auf 6 erhöht.

Verstehen Sie dann auch Beispiel 2? Probieren Sie es aus und überlegen Sie, warum das Ergebnis so ist, wie es ist.

Insbesondere verstehen Sie jetzt auch, warum der folgende Anfängerfehler so gravierend ist:

int x = 0;
x = x++;

Hier passiert folgendes:

  1. x wird eingesetzt, d.h. dort steht x = 0, aber dies wird noch nicht umgesetzt
  2. x wird um eins erhöht, d.h. x hat jetzt den Wert eins
  3. die gesamte Zeile, also x = 0, wird jetzt erst ausgeführt, d.h. x wird auf 0 gesetzt

Im Endeffekt bleibt x also immer Null.

Unterschied ++i und i++

Jetzt kann man sich vorstellen, dass diese Reihenfolge umkehren möchte:

  • (b) Inkrementierung, d.h. der Wert der Variable wird um eins erhöht.
  • (a) Verwendung/Substitution der Variable, d.h. die Variable wird durch ihren aktuellen Wert ersetzt

Das heißt, Sie möchten erst die Variable erhöhen (b) und dann den neuen Wert einsetzen (a). Dies erreichen Sie mit der Formulierung

++i

Sehen wir uns die Beispiele an, um uns davon zu überzeugen, dass das funktioniert:

Beispiel 1:

int foo = 5;
println(++foo);
6

Beispiel 2:

int boo = 10;
int goo = 100 + ++boo;
111

Übungsaufgaben

(a) Muster

Versuchen Sie zu verstehen, wie folgender Code funktioniert:

int x = 0;
int y = 0;

void draw() {
  line(x, 0, x, y);
  x++;
  y++;
}

Verändern Sie den Code um jeweils eines der folgenden Muster zu erzeugen:

(b) Animation mit Modulo

Die Systemvariable frameCount zählt von Beginn Ihres Programms an, wieviele Frames bereits vergangen sind, d.h. die Variable enthält 0, 1, 2, 3 etc.

Verwenden Sie frameCount zusammen mit dem Modulo-Operator, um einen Ball immer wieder von links nach rechts fliegen zu lassen. Ihr draw() besteht dabei lediglich aus zwei Code-Zeilen.

4.6 Systemvariablen

Processing ist so freundlich, Ihnen eine Handvoll Variablen zu "schenken". Das bedeutet: diese Variablen sind immer vorhanden, Sie brauchen sie nicht deklarieren oder setzen, sondern Sie verwenden Sie nur. Zum Beispiel, um die aktuelle Position der Maus rauszukriegen oder um die Bildschirmbreite zu erfahren.

Achten Sie darauf, dass Sie Ihre Variablen nicht so benennen wie Systemvariablen und dass Sie Systemvariablen nie selbst setzen.

Beispiel 1: Größe des Grafikfensters

Hier ein Beispiel, wo Sie ein Rechteck malen, das das linke obere Viertel des Bildschirms besetzt. Sie benutzen die Systemvariablen width und height , welche die Breite und Höhe Ihres Grafikfensters beinhalten:

rect(0, 0, width/2, height/2);

Achten Sie darauf, dass die Systemvariablen width und height erst ab setup() zur Verfügung stehen und auch dort sinnvollerweise erst nach dem Kommando size() abgegriffen werden sollten. Das heißt, dass dieser Code nicht funktionieren wird:

int w = width;  // Problem! width noch nicht gesetzt!
int h = height; // Problem! height noch nicht gesetzt!

void setup() {
}

void draw() {
  ellipse(w/2, h/2, 40, 40);
}

Beispiel 2: Position der Maus

Anderes typisches Beispiel: Ein Kreis soll an Ihrer Maus kleben. Dazu verwenden Sie mouseX und mouseY :

void setup() {
}

void draw() {
  background(255); // wir wollen ja keine Schlieren
  ellipse(mouseX, mouseY, 20, 20);
}

Beispiel 3: Refresh-Geschwindigkeit

Einen Aufruf von draw() nennt man auch einen Frame. Das kommt aus der Filmbranche, ein Frame ist ein Einzelbild eines Films. Normalerweise wird draw() 60 Mal pro Sekunde aufgerufen. Diese 60 nennt man auch die frame rate. Dies ist allerdings nur das Idealverhalten. Wenn Sie viele Programme laufen haben, die viel Rechenzeit benötigen, wird dieser Wert unterschritten. In der Variable frameRate steht der derzeit erreichte Wert. Geben Sie sich das ruhig mal aus:

void setup() {
}

void draw() {
  println(frameRate);
}

Überfordern Sie auch mal den Rechner und sehen, wo die Grenzen liegen:

void setup() {
  frameRate(100000); // setze meine gewünschte frame rate
}

void draw() {
  println(frameRate); // zeige echte frame rate
}

Übersicht: Nützliche Systemvariablen

Hier eine Liste nützlicher Systemvariablen:

Systemvariable/nBeschreibung
mouseX, mouseY aktuelle Position des Mauszeigers
pmouseX, pmouseY Position des Mauszeigers im vorherigen Frame
width, height Pixelgröße des Grafikfensters (Breite und Höhe)
frameRate Anzahl der Frames pro Sekunde, d.h. wie oft draw() aufgerufen wird
frameCount Anzahl der bisherigen Aufrufe von draw() seit Programmstart
keyPressed ob eine Taste derzeit gedrückt ist
key zuletzt gedrückte Taste
keyCode zuletzt gedrückte Taste als Zahlencode (z.B. für Cursortasten)
mousePressed ob ein Mausbutton derzeit gedrückt ist
mouseButton welche der Maustasten aktuell gedrückt ist
displayHeight, displayWidth Maße des gesamten Bildschirms in Pixeln

Übungsaufgaben

(a) Zeichen-Ticker

Zeichnen Sie das Zeichen der zuletzt gedrückten Taste in die Mitte des Fensters.

(Interaktives Feld - erst anklicken.)

Tipp
Verwenden Sie den Befehl text() und die Systemvariable key.

(b) Mitte

Zeichnen Sie einen Kreis genau in die Mitte des Fensters (Größe 111 x 77). Verwenden Sie dazu width und height. Wenn Sie die Fenstergröße ändern, sollte der Kreis immer in der Mitte bleiben, ohne dass Sie den Ellipse-Befehl ändern.