Letztes Update: 3.11.2016
Behandelte Befehle: Kontrollfluss, Flussdiagramm, if, else, else if, UND, ODER, NICHT, boolean, char, keyPressed, keyCode

Lernziele

  • Kontrollfluss eines Programms mit If und Else steuern
  • Einfache Bedingungen formulieren
  • Boolesche Ausdrücke mit AND/OR formulieren
  • Boolesche Variable als Speicher für Bedingungen nutzen
  • Auf verschiedene Tasten reagieren, auch kontinuierlich
  • Kollisionen zwischen Maus und Rechteck/Kreis erkennen

Normalerweise wird Ihr Code Zeile für Zeile, von oben nach unten, ausgeführt. Manchmal möchte man aber eine Zeile - oder einen ganzen Block von Zeilen - aber nur unter einer bestimmten Bedingung durchführen. Sie möchten das Raumschiff nur dann nach links bewegen, wenn eine bestimmte Taste gedrückt ist. Sie möchten eine Erfolgsmeldung nur dann ausgeben, wenn eine bestimmte Punktzahl erreicht wurde. Dafür gibt es die If-Anweisung. Sie erlaubt es, im Code zu "springen" und somit den sogenannten Kontrollfluss zu verändern.

5.1 Einfache If-Anweisung

Video: Einführung in die If-Anweisung (9:49)

Nehmen wir an, Ihr Programm soll einem Prüfling sagen, ob er bestanden hat. Das sei dann der Fall, wenn die Punktzahl (Variable punkte) mindestens 50 beträgt. Im Code wird das so gelöst:

int punkte = 75;

if (punkte >= 50) {
   println("bestanden");
}

Die Zeile mit der Print-Anweisung wird nur unter der Bedingung ausgeführt, dass der Wert von punkte größer oder gleich 50 ist. Ansonsten wird die Zeile übersprungen. Wir ändern also den normalen Kontrollfluss. Das können wir auch als Flussdiagramm visualisieren (man nennt das auch Programmablaufplan):

Ablaufplan If-Anweisung

Im Flussdiagramm stellen Kästen normale Befehle/Anweisungen dar, die Pfeile zeigen, welche Anweisung als nächstes ausgeführt wird. Eine Raute symbolisiert eine Entscheidung: Von hier aus gehen zwei Pfeile aus, einer mit true, einer mit false beschriftet. Man wählt den Pfeil, der der Bedingung entspricht (die entweder zu true oder false evaluiert worden sein muss).

Nach Abarbeiten der If-Anweisung wird der dann folgende Code dann wieder Zeile für Zeile abgearbeitet. Wir sehen das, wenn wir eine weitere Anweisung anfügen:

int punkte = 75;

if (punkte >= 50) {
   println("bestanden");
}
println("tschüs");

Wir können uns das anhand eines sog. Flussdiagramms verdeutlichen. In dem Diagramm zeigen Pfeile die Verarbeitungsreihenfolge an. Rechtecke symbolisieren Befehle/Anweisungen und Rauten Entscheidungen. Bei Rauten sind daher die ausgehen Pfeile mit möglichen Ausgängen der jeweiligen Entscheidung gekennzeichnet (hier: true oder false).

Ablaufplan If-Anweisung mit weiterer Anweisung

In diesem Fall wird "tschüs" immer ausgegeben, unabhängig davon, ob die Bedingung erfüllt ist oder nicht. Wir sind wieder im "regulären" Kontrollfluss.

Bedingung

Die allgemeine Form der If-Anweisung ist:

if (BEDINGUNG)
   ANWEISUNG

Eine Anweisung ist entweder eine einzelne Code-Zeile (Befehl, Variablenzuweisung ...) oder - wie in unseren Beispielen - ein Code-Block, der mit geschweiften Klammern markiert ist. Auf Code-Blocks gehen wir im nächsten Abschnitt ein.

Eine Bedingung ist ein sogenannter boolescher Ausdruck, benannt nach dem britischen Mathematiker George Boole. Ein boolescher Ausdruck ist ein Ausdruck, der nach Auswertung immer entweder wahr (true) oder falsch (false) ist. Die zwei einfachsten booeschen Ausdrücke sind true und false. Sie können z.B. schreiben:

if (true) {
    println("immer");
}

Die obige Anweisung wird immer ausgeführt, was natürlich wenig sinnvoll ist (man kann das if dann auch einfach weglassen), aber es zeigt, dass das Schlüsselwort true (genauso wie false) eine vollwertige Bedigung sein kann.

Eine weiterer boolescher Ausdruck ist ein numerischer Vergleich. Beispiele:

10 > 100
x > 5 + y
41 == foo
x != y
33 < boo

Auf beiden Seiten des Vergleichs

  • können beliebig komplexe arithmetische Ausdrücke stehen.
  • können Variablen vorkommen, diese werden bei der Abarbeitung durch ihre aktuellen Werte ersetzt.

Beachten Sie insbesondere das "gleich". Es muss mit doppeltem Gleichheitszeichen geschrieben werden! Es ist ein häufiger Anfängerfehler hier nur ein einfaches Gleichheitszeichen zu setzen, da in diesem Fall keine Fehlermeldung ausgegeben wird! Es wird dann nämlich eine Zuweisung vorgenommen, d.h. die Variable, die Sie nur "testen" wollen, wird verändert!

x == 5
z == k
42 == foo
Merken Sie sich: Der Test auf Gleichheit in Bedingungen erfordert ein doppeltes Gleichheitszeichen ==.

Im Vergleich zu den Vergleichsoperatoren, die man aus der Schule kennt, ist zu beachten:

  • Für "kleiner gleich" schreiben Sie <= und zwar in genau dieser Reihenfolge. Ähnlich für "größer gleich".
  • Für "gleich" schreiben Sie ein doppeltes Gleichheitszeichen.
  • Für "ungleich" wird != geschrieben. Das Ausrufezeichen ist nämlich für Negation zuständig.

Code-Block

Die geschweiften Klammern in den obigen Beispielen markieren jeweils einen sogenannten Code-Block. Code-Blocks erlauben uns, viele Zeilen "zusammenzupacken" und bei der If-Anweisung als Aktion zu definieren, die ausgeführt wird, wenn die Bedingung wahr ist.

Code-Blocks werden uns noch in vielen anderen Zusammenhängen begegnen, z.B. bei Funktionen, Schleifen und Klassen. Beachten Sie, dass die Zeilen innerhalb des Code-Blocks eingerückt sind und zwar alle gleich, so dass man direkt sieht, dass diese Zeilen zusammengehören. In der Processing-Umgebung können Sie das automatisch durchführen lassen im Menupunkt Edit > Auto Format oder mit der Tastenkombination CMD+T (Mac) bzw. STRG+T (Windows).

Die Einrückung ist wichtig, weil ein Code-Block weitere Code-Blöcke enthalten kann. Das heißt die Blöcke können verschachtelt sein. Hier ein Beispiel:

int punkteTest = 75;
int zusatzTest = 85;

if (punkteTest >= 50)
{
   println("Test ist bestanden!");
   if (zusatzTest >= 50)
   {
      println("Zusatztest ist AUCH bestanden!");
   }
}

Der Else-Teil

Wollen wir auch eine Aktion immer dann ausführen, wenn die Bedingung nicht erfüllt ist, können wir noch einen Else-Teil (else heißt "sonst") anfügen:

int punkte = 75;

if (punkte >= 50) {
   println("bestanden");
} else {
   println("durchgefallen");
}
println("tschüs");

Auch hier können wir den Kontrollfluss mit einem Flussdiagramm verdeutlichen:

Flussdiagramm If-Anweisung mit Else

Eine wichtige Erkenntnis ist, dass, egal ob die Bedingung true oder false ist, immer Code ausgeführt wird (im Diagramm entweder der rechte Ast oder der linke Ast).

Die If-Else-Anweisung ist als ein einziges, zusammengehöriges Konstrukt zu betrachten, der zwei Code-Blöcke enthält. Nach dem If-Else geht es regulär Zeile für Zeile weiter, hier folgt also auch immer das "tschüs".

Abprallen & Geschwindigkeit

Nehmen wir an, Sie animieren einen Ball, von links nach rechts zu fliegen. Wenn er rechts aus dem Bildschirm tritt, soll er links wieder auftauchen:

Das Basisprogramm sieht zunächst mal so aus:

int x = 0;

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

In dieser Version fliegt der Ball genau einmal über den Bildschirm und verschwindet dann auf Nimmerwiedersehen.

Überlegen Sie, was folgender Satz bedeutet:

Wenn er rechts aus dem Bildschirm tritt,
soll er links wieder auftauchen.

Der erste Teilsatz ist eine Bedingung, das sieht man an dem "Wenn". Wie übersetzen Sie diese Bedingung in Code? Sie betrachten sich Ihre Variablen, das ist in diesem Fall x und überlegen, wann die Bedingung

Wenn er rechts aus dem Bildschirm tritt

erfüllt ist. Die Antwort: wenn das x genau so groß wird wie das Fenster breit ist, beim normalen 100x100-Fenster also:

x == 100

Denken Sie immer daran, bei Vergleichen das doppelte Gleichheitszeichen zu verwenden.

Noch besser ist es, diese Bedigung allgemein zu halten, denn es könnte sein, dass Sie das Grafikfenster größer oder kleiner stellen. Verwenden Sie die Systemvariable width, die die aktuelle Breite des Grafikfensters enthält.

x == width

Kommen wir zum zweiten Teilsatz:

..., soll er links wieder auftauchen.

Was bedeutet es, dass der Ball links auftaucht? Ganz einfach: dass der x-Wert wieder auf Null gesetzt wird. Wir testen also in unserem Programm in jedem Durchlauf von draw(), ob das x == width ist und, wenn das der Fall ist, setzen wir x auf Null:

int x = 0;

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

  if (x == width) {
      x = 0;
  }
}

Geschwindigkeit

Die Bewegung des Balls wird durch das x++ verursacht. Bei jedem Durchlauf von draw() wird das x um genau 1 erhöht. Wenn wir das x jedesmal um 3 erhöhen würden, wäre der Ball schneller. Um hier flexibel zu sein, führen wir eine neue Variable als Geschwindigkeit mit Namen speed ein. Der Wert dieser Variable wird jetzt auf das x addiert:

int x = 0;
int speed = 3;

void draw() {
  background(0);
  ellipse(x, 50, 20, 20);
  x = x + speed;

  // VORSICHT!
  if (x == width) {
    x = 0;
  }
}

Dieser Code funktioniert nicht! Warum? Rechnen Sie mal nach, welche Werte das x annimmt (lassen Sie sich das x doch auf der Konsole ausgeben):

0
3
6
12
...
93
96
99
102
105

Das Problem ist, dass x nicht genau 100 wird, sondern darüber hinweg springt. Deshalb müssen wir unsere If-Bedingung allgemeiner fassen:

if (x >= width) {
    x = 0;
}

Das komplette Programm (ob man hier > oder >= verwendet, spielt keine Rolle):

int x = 0;
int speed = 3;

void draw() {
  background(0);
  ellipse(x, 50, 20, 20);
  x = x + speed;

  if (x > width) {
    x = 0;
  }
}

Rückwärts fliegen

Indem Sie den Wert negativ machen, können Sie den Ball auch rückwärts fliegen lassen:

int x = 100;
int speed = -2;

void draw() {
  background(0);
  ellipse(x, 50, 20, 20);
  x = x + speed;

  if (x < 0) {
    x = width;
  }
}

Ihnen ist hoffentlich aufgefallen, dass die If-Anweisung verändert wurde. Auf deutsch lautet die Anweisung jetzt:

Wenn der Ball links aus dem Bildschirm tritt,
soll er rechts wieder auftauchen.

Links aus dem Bildschirm treten, übersetzt sich zu x < 0 und rechts wiederauftauchen realisiert man mit x = width. Logisch, oder?

Coding Style

Bitte achten Sie darauf, wo Leerzeichen und Zeilenumbrüche gemacht werden, und wo nicht.

if (x > 100) {
  x = 0;
}

Alternativ:

if (x > 100)
{
  x = 0;
}

Denken Sie auch daran, Ihre Code mit Bearbeiten > Autoformatierung von Processing korrekt einrücken zu lassen (Tastenkürzel STRG+T bzw. CMD+T).

Fingerübungen

Einfaches If

Schreiben Sie jeweils kurze Programme (statischer Modus) für folgende Bedingungen und testen Sie die Programme mit verschiedenen Variablenwerten:

  1. Gegeben sei eine int-Variable foo. Wenn diese größer als 50 ist, soll "OK" ausgegeben werden.
  2. Gegeben seien zwei int-Variablen a und b. Wenn a um mindestens 10 größer ist als b, soll "a gewinnt" ausgegeben werden.
  3. Gegeben seien vier float-Variablen a, b, c, d. Wenn die Summe von a und b größer ist als die Summe von c und d, dann soll "a/b gewinnen" ausgegeben werden
  4. Gegeben seien drei int-Variablen x, y, z. Wenn die Summe von x und y genau doppelt so groß ist wie z ist, soll "passt" ausgegeben werden.

If und Else

Schreiben Sie jeweils kurze Programme (statischer Modus) für folgende Bedingungen und testen Sie die Programme mit verschiedenen Variablenwerten:

  1. Gegeben sei eine boolesche Variable istKorrekt. Wenn diese wahr ist, soll "richtig" ausgegeben werden, sonst "falsch".
  2. Gegeben sei die int-Variable zahl. Wenn die Zahl gerade ist, soll "gerade" ausgegeben werden, sonst "ungerade".
Tipp
Für die Frage gerade/ungerade überlegen Sie, ob Sie mit dem Modulo (Rest) weiterkommen.

Übungsaufgaben

(a) Verschachteltes If

Gegeben seien drei int-Variablen a, b und x.

Wenn x zwischen den Zahlen a und b liegt, sollen Sie "drin" auf der Konsole ausgeben.

Gehen Sie davon aus, dass a kleiner als b ist!

Schreiben Sie zunächst eine If-Anweisung und dann eine zweite innerhalb der ersten.

Hinweis: Verwenden Sie hier kein UND.

(b) Klausurnoten

Sie haben eine Punktzahl gegeben:

int punkte = 10;

Schreiben Sie ein Programm, dass bei einer Punktzahl von mindestens 5 ausgibt:

Bestanden!
Auf Wiedersehen.

Bei 4 oder weniger Punkten, wird ausgegeben:

Leider durchgefallen :(
Auf Wiedersehen.

Hinweis: Verwenden Sie if-else und vermeiden Sie, dass Code-Zeilen mit der gleichen Anweisung zwei Mal auftreten.

Im nächsten Schritt erweitern Sie Ihr Programm, so dass bei verschiedenen Punktständen jeweils folgendes ausgegeben wird:

0 - 4:   Durchgefallen
5 - 9:   Bestanden
10 - 15: Bestanden mit Auszeichnung

Testen Sie Ihr Programm mit den Werten 0, 15 und 25.

Tipp
Sie müssen eine weitere If-Anweisung innerhalb des Code-Teils der ersten If-Anweisung platzieren.

(c) Stehenbleiben

Ändern Sie das folgende Programm derart, dass der Ball stehenbleibt, sobald er die Mitte erreicht.

int x = 0;

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

  if (x > width) {
    x = 0;
  }
}

(d) Hoch-runter

Ändern Sie das obige Programm derart, dass der Ball von oben nach unten fliegt.

(e) Wachsen

Der Ball fliegt wiederholt von links nach rechts. Bei jedem Durchlauf soll der Ball etwas größer sein.

(f) Wachsen 2

Ein mittig platzierter Kreis wird stetig größer, bis er den Rand des Fensters erreicht, dann beginnt er wieder als ein kleiner Kreis zu wachsen.

(g) Mutierender Mousezeiger

Folgendes Programm erzeugt einen Mauszeiger in Form eines Kreises. Verändern Sie das Programm derart, dass in der rechten Bildschirmhäfte der Mauszeiger zur gefüllten, schwarzen Box wird (mittig zum Mauszeiger) und in der linken wieder zum leeren Kreis.

void draw() {
  background(255);
  ellipse(mouseX, mouseY, 20, 20);
}

(h) Timer

Sie möchten ein Programm schreiben, dass jede Sekunde einen Punkt auf die Konsole schreibt (verwenden Sie print statt println), d.h. nach 10 Sekunden sehen Sie:

..........

Schreiben Sie zunächst ein Programm, das alle 60 "Zyklen" einen Punkt schreibt. Ein Zyklus bedeutet, dass ein Mal draw() aufgerufen wurde. Verwenden Sie eine Zählervariable:

int count = 0;

Je nach Rechner und aktueller Rechnerbelastung wir draw() auch seltener als 60 Mal aufgerufen. Die Systemvariable frameRate (float) gibt an, wie oft draw() aktuell pro Sekunde aufgerufen wird. Nutzen Sie dies, um ziemlich genau 1x pro Sekunde einen Punkt zu malen.

Hinweis: Nutzen Sie nicht die Funktion frameRate(), sondern die Systemvariable frameRate wie oben angegeben.

Zusammenfassung

Eine If-Anweisung erlaubt es, eine oder mehrere Code-Zeilen (einen Code-Block) nur unter einer ganz bestimmten Bedingung auszuführen.

  • Eine Bedingung wird durch einen booleschen Ausdruck definiert. Dies ist ein Ausdruck, der zu true oder false ausgewertet werden kann. Die Schlüsselworte true und false sind jeweils (triviale) boolesche Ausdrücke. Wichtiger sind arithmetische Vergleiche wie x > 5 oder 10 == y. WICHTIG: Immer doppeltes Gleichheitszeichen beim Vergleich nehmen!
  • Ein Code-Block ist eine Anzahl von Anweisungen (können also mehrere Zeilen sein), von von geschweiften Klammern umgrenzt sind. Die Code-Zeilen innerhalb der Klammern sind in Processing alle gleich weit eingerückt, so dass man leicht erkennt, in welchem Block sich eine Zeile befindet.
  • Möchte man Code-Zeilen genau dann ausführen, wenn die Bedingung nicht erfüllt ist, so hängt man mit Hilfe von else einen zweiten Code-Block an, der genau dann ausgeführt wird.

5.2 Else-If

Video: Else if (9:24)

Wenn Sie größere Fallunterscheidungen haben, bietet sich das Else-If an.

Möchten Sie zum Beispiel verschiedene Aktionen abfeuern, je nachdem ob ein Prüfling mehr als 75, mehr als 50 oder weniger als 50 hat, müssten Sie zwei If-Anweisungen verschachteln. Die äußere If-Anweisung unterscheidet zwischen >50 und <=50, die="" innere="" If-Anweisung="" teilt="" den="" Bereich="">50 dann nochmal in >75 und <=75. <="">

int punkte = 55;

if (punkte > 50) {
  if (punkte > 75) {
    println("Bestnote!");
  } else {
    println("Bestanden.");
  }
} else {
  println("Leider durchgefallen"); // Else-Teil
}

Dieser Code ist schwer zu lesen und je mehr Fälle Sie haben, umso komplexer wird der Code.

Das Else-If erlaubt Ihnen, eine zweite Bedingung hinzuzufügen. Diese Bedingung wird nur dann geprüft, wenn die erste falsch ist. Das schlussendliche Else greift dann, wenn beide Bedingungen falsch waren.

int punkte = 55;

if (punkte > 75) {
   println("Bestnote!");
} else if (punkte > 50) {
   println("Bestanden.");
} else {
   println("Leider durchgefallen"); // Else-Teil
}

Es können beliebig viele Else-If-Teile eingefügt werden:

int punkte = 55;

if (punkte > 90) {
   println("Bestnote!");
} else if (punkte > 75) {
   println("Sehr gut.");
} else if (punkte > 50) {
   println("Bestanden.");
} else {
   println("Leider durchgefallen"); // Else-Teil
}
println("tschüs");

Processing testet zuerst die erste Bedingung. Falls diese falsch ist, wird das erste Else-If getestet. Falsch dies falsch ist, wird das nächste Else-If getestet und so fort. Sind alle diese Bedingungen falsch, so wird der Else-Teil ausgeführt. Danach wird zum normalen Kontrollfluss zurückgekehrt.

Allgemein kann man das so schreiben:

if (BEDINGUNG-1)
   ANWEISUNG-1
else if (BEDINGUNG-2)
   ANWEISUNG-2
else if (BEDINGUNG-3)
   ANWEISUNG-3
...
else
   ANWEISUNG-N

Flussdiagramm

Als Flussdiagramm sieht das so aus:

Flussdiagramm If-Else-If

Man sieht hier deutlich, dass nur genau eine Option (d.h. ein Code-Block) ausgeführt wird. Danach wird zum normalen Kontrollfluss zurückgekehrt.

Coding Style

Bitte achten Sie darauf, wo Leerzeichen und Zeilenumbrüche gemacht werden, und wo nicht.

if (foo > 200) {
  println("foo");
} else if (foo > 100) {
  println("bar");
} else {
  println("sonst");
}

Alternativ:

if (foo > 200)
{
  println("foo");
} else if (foo > 100)
{
  println("bar");
} else
{
  println("sonst");
}

Denken Sie auch daran, Ihre Code mit Bearbeiten > Autoformatierung von Processing korrekt einrücken zu lassen (Tastenkürzel STRG+T bzw. CMD+T).

Übungsaufgaben

(a) Klausurnoten

Lösen Sie eine ähnliche Aufgabe wie die im letzten Abschnitt mit if-else-if. Sie haben eine Punktzahl gegeben:

int punkte = 10;

Schreiben Code, so dass bei verschiedenen Punktständen ausgegeben wird, wie die Klausur ausfällt:

0  - 10: Durchgefallen
11 - 20: Naja
21 - 30: Mittelgut
31 - 40: Gut
41 - 50: Super

Testen Sie Ihr Programm mit den Werten 0, 15, 25, 35, 45.

(b) Lauftext

Sie schreiben eine Textanzeige, die bei Mausklick zum nächsten Wort wechselt. Das erste Wort ist "Hallo":

int zustand = 0;

void setup() {
  textAlign(CENTER);
  textSize(20);
}

void draw() {
  background(0);
  if (zustand == 0) {
    text("Hallo", width/2, height/2);
  }
}

Ergänzen Sie den Code, dass bei jedem Mausklick ein neues Wort angezeigt wird. Nach "Hallo" kommt "mein", "Name", "ist", "Hase", "Tschüs". Danach geht es wieder von vorn los.

(Interaktives Feld:)

Nutzen Sie die Variable zustand und eine if-else-if Konstruktion.

5.3 Boolesche Ausdrücke

Video: Boolesche Ausdrücke mit AND und OR (8:38)

Um komplexere Sachverhalte auszudrücken, müssen Sie evtl. mehrere Vergleiche kombinieren. Solche Kombinationen sind wieder boolesche Ausdrücke und können also als Bedingung für das If verwendet werden.

Die genannten Kombinationen werden über sogenannte logische Operatoren hergestellt.

Logisches UND

Nehmen wir an, Sie möchten prüfen, ob die Variable x zwischen 10 und 20 liegt, in der Schule schreibt man einfach 10 < x < 20. In Processing müssen Sie das in zwei Vergleiche zerlegen, nämlich in 10 < x und x < 20. Damit "10 < x < 20" wirklich gilt, müssen beide Vergleich wahr sein. Diese Kombination nennt man das logische UND (engl. AND).

Das logische UND nennt man auch einen logischen Operator. Im Code schreibt man für den UND-Operator ein doppeltes "käufmännisches Und", also && :

int x = 5;

if (10 < x && x < 20) {
   println("Yes!");
}

In der mathematischen Logik nennt man diese Operation auch Konjunktion und das Symbol für UND sieht so aus:

Das logische UND ist verwandt mit dem Konzept der Schnittmenge. Wenn Sie sich die zwei Mengen 10 < x und x < 20 vor Augen führen, dann ist (10 < x && x < 20) genau die Schnittmenge.

Logisches ODER

Der zweite wichtige Operator ist das logische ODER (engl. OR). Nehmen wir an, Sie wollen testen, ob die x-Koordinate eines Objekts den Bildschirmbereich verlässt, der 1024 Pixel breit ist. Dann testen Sie ob x < 0 ist oder x >= 1024.

Das logische ODER schreibt man im Code mit zwei senkrechten Strichen ||

int x = 5;

if (x < 0 || x >= 1024) {
   println("Oops!");
}

In der mathematischen Logik nennt man diese Operation auch Disjunktion und das Symbol für ODER sieht so aus: V

Das logische ODER ist verwandt mit dem Konzept der Vereinigungsmenge. Wenn Sie sich wieder die zwei Mengen 10 < x und x < 20 anschauen, dann ist (10 < x || x < 20) genau die Vereinigungsmenge.

Und wo ist dieses Zeichen | auf meiner Tastatur...?

Auf dem Mac mit ALT+7, unter Windows mit Alt Gr + <.

Trivia

Das Zeichen | wird in Unix-Kreisen auch pipe genannt, weil damit die gleichnamige Operation in einer Unix-Shell gekennzeichnet wird.

Negation

Mit dem Ausrufezeichen negieren Sie einen Ausdruck, d.h. Sie kehren den Wert um (true statt false bzw. false statt true). Das Ausrufezeichen nennt man auch den logischen Negationsoperator oder das logische NICHT (engl. NOT). Im Unterschied zu UND/ODER steht dieser Operator nicht zwischen zwei Ausdrücken, sondern vor einem einzigen Ausdruck.

Wenn Sie z.B. den Vergleich x < 10 negieren wollen, schreiben Sie ! (x < 10) . Das entspricht dann offensichtlich x >= 10 , d.h. das könnte man eigentlich auch direkt so (ohne Negation) hinschreiben, aber für komplexere Ausdrücke ist die Negation manchmal bequemer oder lesbarer.

Nehmen wir an, Sie wollen das Bespiel oben "Objekt verlässt Bildschirm" einfach negieren. Das heißt, Sie wollen Code ausführen für den Fall, dass das Objekt noch auf dem Bildschirm ist, dann schreiben Sie:

int x = 5;

if (! (x < 0 || x >= 1024)) {
   println("Bin auf dem Schirm!");
}

In der mathematischen Logik sieht das Symbol für Negation so aus: ¬

Sollten Sie sich fragen, wie man die obige Bedingung ohne Negation schreibt, kommen Sie auf diesen Ausdruck: x >= 0 && x < 1024 . Das heißt, wenn man einen Ausdruck mit logischem ODER negiert, dann negiert man beide Einzelteile und man "dreht den Operator um", d.h. aus ODER wird UND und umgekehrt. Das nennt man das De Morganschen Gesetz.

! (x < 0 || x >= 1024)

   entspricht

! (x < 0) && ! (x >= 1024)

   entspricht

x >=0 && x <1024

Komplexe Ausdrücke und Präzedenz

Sie können beliebig viele logische Ausdrücke mit UND/ODER kombinieren und mit NICHT negieren.

Wie wird ein solcher Ausdruck ausgewertet? Nehmen wir ein Beispiel mit zwei logischen Operatoren:

int k = 5;
float f = 2.5;
int t = 30;

if (k > 0 && f < 10.0 || t > 100) {
  println("hurra");
}

Processing nimmt sich die Bedingung:

k > 0 && f < 10.0 || t > 100

Und setzt zunächst die Variablenwerte ein:

5 > 0 && 2.5 < 10.0 || 30 > 100

Dann werden die einzelnen Vergleiche aufgelöst:

true && true || false

Jetzt gehen wir von links nach rechts vor und lösen einen Operator nach dem nächsten, also zunächst das true && true, das wird zu true. Anschließend haben wir noch:

true || false

Dies wird wiederum aufgelöst zum Ergebnis:

true

In unserem Beispiel konnten wir von links nach rechts rechnen, aber das gilt nicht immer, denn der UND-Operator hat Priorität vor ODER.

Sehen wir uns das obige Beispiel leicht modifiziert an: das UND steht jetzt hinten, das ODER vorn:

k > 0 || f < 10.0 && t > 100

Wir setzen die Variablenwerte ein:

5 > 0 || 2.5 < 10.0 && 30 > 100

Und lösen die Vergleiche auf:

true || true && false

Jetzt kommt der entscheidende Unterschied: Statt von links nach rechts zu rechnen, müssen wir das UND vorziehen (das wird Präzedenz genannt):

true || true && false

Wir lösen als zunächst true && false auf zu false:

true || false

Dies wird wiederum aufgelöst zum Ergebnis:

true

Allgemein gesprochen verwendet Java bei booleschen (logischen) Ausdrücken ähnliche Regeln wie bei arithmetischen Ausdrücken (also Rechnungen mit +, -, *, /). Dabei gelten folgende Regeln:

OperatorPrioritätentspricht
!3unäres - (wie bei -3)
&&2*, /
||1+, -

In der Tabelle bedeutet eine höhere Priorität, dass der Operator als erstes ausgewertet wird. Das bedeutet: der Negationsoperator wird immer zuerst ausgewertet. Ferner gilt: ein UND wird immer vor ODER ausgewertet.

Wenn Sie folgenden Ausdruck vor sich haben:

a || b && !c || d

Wird dieser so ausgewertet, als gäbe es folgende Klammern:

(a || (b && (!c))) || d

Anwendungsbeispiel

Sie schauen uns das Programm von oben, inklusive Geschwindigkeit, nochmal an:

Dank der Geschwindigkeit kann der Ball sowohl nach rechts (speed = 1) als auch nach links (speed = -1) fliegen. Daher möchten wir jetzt, dass der Ball von der rechten Wand "abprallt". Das gleiche gilt natürlich auch für die linke Wand.

Man könnte das so formulieren:

Wenn der Ball den rechten Rand erreicht,
soll er nach links weiterfliegen.

Der erste Teilsatz übersetzt sich wieder zu der Bedingung x > width. Der zweite Satz "nach links weiterfliegen" lässt sich realisieren, indem man die Geschwindigkeit auf -1 setzt:

if (x > width) {
    speed = -1;
}

Analog für den linken Rand:

Wenn der Ball den linken Rand erreicht,
soll er nach rechts weiterfliegen.

Im Code:

if (x < 0) {
    speed = 1
}

Verallgemeinerung

Wenn wir die Lösung allgemeiner betrachten, sehen wir, dass in beiden Fällen einfach das Vorzeichen der Geschwindigket umgedreht wird. Aus 1 wird -1 und aus -1 wird 1. Im Code lässt sich das Umdrehen des Vorzeichens ganz elegant formulieren:

speed = -speed;

Das schöne an dieser Formulierung ist, dass es egal ist, welcher Wert in speed steht, egal ob 1 oder 3000. Wenn der Wert negativ ist, wird er positiv. Ist er positiv, wird er negativ.

Der Code würde also wie folgt aussehen:

int x = 0;
int speed = 1;

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

  if (x > width) {
    speed = -speed;
  }

  if (x < 0) {
    speed = -speed;
  }
}

Es fällt auf, dass in beiden If-Fällen der exakt gleiche Code ausgeführt wird, also man könnte auch sagen: wenn x > width oder x < 0 der Fall ist, dann drehe das Vorzeichen um. Klarer Fall für eine boolesche Verknüpfung:

int x = 0;
int speed = 3;

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

  if (x < 0 || x > width) {
      speed = -speed;
  }
}

Ein wichtiger Vorteil zu unserer ersten Lösung: wir können wieder beliebige Geschwindigkeitswerte nehmen, z.B. eben 3:

Übungsaufgaben

(a) Human Resources

Sie sind Personaler und möchten jemanden einstellen, der/die nicht zu jung ist (min. 20 Jahre) und nicht zu alt (max. 60 Jahre). Außerdem soll die Abschlussnote stimmen (besser als 3.0).

Ergänzen Sie den Code so, dass Ihr Programm "Einstellen!" ausgibt, wenn die obigen Bedingungen stimmen oder "Lieber nicht.", wenn nicht.

int alter = 21;
float note = 1.7;

Testen Sie Ihr Programm auch mit anderen Werten (alter=61, note=2.0 oder alter=33, note=3.3).

(b) Pulsieren

Ein Kreis mit Durchmesser 50 wird stetig größer, bis er den Rand des Fensters erreicht, dann verkleinert er sich bis Durchmesser 50 usw.

Sehen Sie sich oben im Skript das Anwendungsbeispiel mit dem "an der Wand abprallen" an. Beim Abprallen haben Sie die x-Koordinate, die entweder größer oder kleiner wird. Bei der Aufgabe hier haben Sie genau das gleiche mit dem Durchmesser.

Wichtig: Achten Sie darauf, dass der Durchmesser nicht negativ wird. Geben Sie Ihre Variable für den Durchmesser zur Kontrolle auf der Konsole aus. Sie sollte nie negativ werden.

(c) Pulsierende Farbe

Ein schwarzer Kreis wird stetig weißer bis er ganz weiß ist, dann wieder schwärzer, bis er ganz schwarz ist usw.

Auch hier ist das Schema wie in der vorigen Aufgabe: ein veränderlicher Wert (Grauwert) und eine Geschwindigkeit, die sich "umdreht".

Zusammenfassung

  • Zwei boolesche Ausdrücke (wie z.B. die Vergleiche x > 5 und x < 10) lassen sich mit den logischen Operatoren UND bzw. ODER kombinieren. Das Gesamte ist wieder ein boolescher Ausdruck.
  • Wir haben drei logische Operatoren kennen gelernt:
    • UND: Der Ausdruck A && B (A und B) ist genau dann wahr, wenn sowohl A als auch B wahr sind.
    • ODER: Der Ausdruck A || B (A oder B) ist genau dann wahr, wenn entweder A oder B (oder beide) wahr sind.
    • NICHT: Der Ausdruck !A ist genau dann wahr, wenn A falsch ist.
  • Ein booleschen Ausdrucks ist also entweder
    • das Schlüsselwort true oder false
    • ein Vergleich von Werten (z.B. == oder <=<>)
    • die logische Verknüpfung von zwei booleschen Ausdrücken mit && oder ||
    • die Negation eines booleschen Ausdrucks mit !
    Interessant an dieser Definition ist, dass boolesche Ausdrücke innerhalb von booleschen Ausdrücken vorkommen können (wie z.B. in foo && (bar || doo)). Das ist sehr praktisch, weil man mit wenigen Regeln auch sehr komplexe, verschachtelte Konstrukte erfassen kann. Man nennt eine solche Definition rekursiv.

5.4 Boolesche Variablen

Sie kennen jetzt Bedingungen in If-Anweisungen und sogar boolesche Ausdrücke mit AND und OR. Boolesche Variablen sind ganz primitive Zeitgenossen, da sie lediglich einen von zwei möglichen Werten speichern können: true oder false .

Wozu braucht man sowas? In Processing wollen Sie sich zum Beispiel einen Zustand merken. Wenn Sie zum Beispiel eine Animation haben, möchten Sie sich merken, ob die Animation laufen soll (play) oder anhalten (pause).

Nehmen wir mal eine einfache Animation:

int x = 0;

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

  // immer wieder von links reinkommen
  if (x >= 100) {
    x = 0;
  }
}

Um einen einfachen play/pause Mechanismus zu implementieren, führen wir eine boolesche Variable play ein. Wenn diese true ist, soll die Animation laufen. Wenn sie false ist, nicht:

int x = 0;
boolean play = true;

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

  // NEU: bewege dich nur, wenn play true ist
  if (play) {
    x++;
  }

  // immer wieder von links reinkommen
  if (x >= 100) {
    x = 0;
  }
}

Es hat sich nicht viel geändert, aber Sie können die Variable play von Hand auf false setzen, dann bleibt der Ball links stehen. Beachten Sie, dass Sie nicht play == true in die Bedingung schreiben müssen (obwohl das auch funktionieren würde). Wenn play true ist, wird direkt true eingesetzt - das gleiche gilt für den Fall, dass play false ist. Keine Notwendigkeit also, nochmal == true hinzuschreiben.

Jetzt müssen Sie nur noch "zur Laufzeit", also während das Programm läuft, die Variable play umsetzen. Das funtioniert, indem Sie die folgende Funktion hinzufügen:

void keyPressed() {
  if (play) {
    play = false;
  } else {
    play = true;
  }
}

Interaktives Feld:(erst auf das graue Feld klicken, dann Taste)

Sie setzen also die Variable play in Abhängigkeit von ihrem aktuellen Zustand, nämlich auf ihr 'Gegenteil'. In der Logik nennt man das auch die Negation ihres Zustands. Das kann man ganz elegant formulieren als:

void keyPressed() {
  play = !play; // setze play auf seine eigene Negation
}

Das Negationszeichen funktioniert also ähnlich wie das Minuszeichen in der Arithmetik. Natürlich sind beide Code-Varianten völlig legitim und in Ordnung.

Boolesche Variablen und boolesche Ausdrücke

In eine boolesche Variable kann ein beliebiger boolescher Ausdruck per Zuweisung hineingetan werden. Im folgenden Beispiel wird der Ball in der x-Zone 30-70 rot gefärbt:

int x = 0;
boolean inTheZone = false;

void draw() {
  background(255);

  // boolescher Ausdruck
  inTheZone = x > 30 && x < 70;

  if (inTheZone) {
    fill(255, 0, 0); // rot
  }
  else {
    fill(255); // weiß
  }

  ellipse(x, 50, 20, 20);
  x++;
}

Auch hier zeigt sich der Vorteil der Varible erst, wenn die Information an anderer Stelle bzw. zu anderer Zeit verwendet wird. Wollen Sie etwa per Tastendruck den Ball wieder auf x=0 setzten unter der Bedingung, dass der Ball in 'der Zone' ist, dann fügen Sie hinzu:

void keyPressed() {
  if (inTheZone) {
    x = 0;
  }
}

Boolesche Variablen als Teil boolescher Ausdrücke

Boolesche Variablen können natürlich auch Bestandteil von booleschen Ausdrücken sein. Hier zwei Beispiele:

boolean foo = true;
boolean goo = false;
int x = 10;

boolean ausdruck1 = foo && goo;
boolean ausdruck2 = foo || x > 5;

println(ausdruck1);
println(ausdruck2);

Übungsaufgaben

(a) Umschalten

Ergänzen Sie den folgenden Code so, dass die Variable foo erst auf false und dann auf true geschaltet wird. An beiden Stellen soll der gleiche Code stehen!

boolean foo = true;

// Ihr Code

println(foo);

// Gleicher Code wie oben

println(foo);

Die Ausgabe soll sein:

false
true

Hinweis: Es gibt zwei Möglichkeiten. Entweder Sie verwenden ein if-else oder Sie überlegen sich, wie Sie eine boolesche Variable "umkehren" - das funktioniert so ähnlich wie das Umkehren des Vorzeichens bei einer Zahl.

(b) Now you see me...

Schreiben Sie ein Programm, dass einen schwarz gefüllten Kreis malt. Wenn man auf die Maustaste drückt, verschwindet er. Beim nächsten Klick erscheint er usw.

Interaktives Feld:(auf das Feld klicken)

Tipp
Verwenden Sie eine boolesche Variable, um sich zu merken, ob Ihr Kreis sichtbar ist (zu Beginn true). Nutzen Sie Aufgabe (a), um die Variable beim Mausklick umzuschalten.

(c) Kreis oder Box

Wo immer ist Mauszeiger ist, wird ein weiß gefüllter Kreis gezeichnet (Durchmesser 30). Wenn Sie auf die Maustaste drücken, wird stattdessen ein schwarz gefülltes Quadrat gezeichnet (Kantenlänge 20). Wenn Sie wieder die Taste drücken, kommen wieder Kreise usw.

Interaktives Feld:(erst auf das graue Feld klicken)

Tipp
Merken Sie sich in einer booleschen Variablen, ob Sie einen Kreis oder ein Rechteck zeichnen.

(d) horizontal oder vertikal

Ein Kreis fliegt von links nach rechts und erscheint wieder am linken Rand, wenn er rechts rausfliegt. Wenn man die Maustaste drückt, fliegt der Kreis nach unten und erscheint wieder oben, wenn er den unteren Rand erreicht. Beim nächsten Klick fliegt er wieder horizontal usw.

Interaktives Feld:(erst auf das graue Feld klicken)

Zusammenfassung

Boolesche Variablen können einen binären Zustand speichern, d.h. eine Variable enthält entweder den Wert true oder den Wert false. Eine boolesche Variable kann z.B. in einer If-Bedingung verwendet werden.

Ein Anwendungsbeispiel ist der Unterschied zwischen "Play" und "Pause" bei einer Animation. Dies kann in einer booleschen Variable play gespeichert werden. Hier würde true bedeuten "Objekt bewegt sich" und false "Objekt bewegt sich nicht".

Eine boolesche Variable kann mit dem Negationsoperator invertiert werden, z.B. die Variable play

play = !play;

Eine boolesche Variable ist selbst ein boolescher Ausdruck, kann also allein in einer If-Bedingung stehen:

if (play) {
  x++;
}

5.5 Anwendung: Interaktion

Interaktion mit Tasten

Nachdem wir die If-Anweisung kennengelernt haben, können wir wesentlich differenzierter mit Benutzereingabe umgehen. Wir können z.B. unterscheiden, welche Taste gedrückt wurde.

Sie wissen, dass Sie auf einen Tastendruck reagieren können, indem Sie die Funktion keyPressed() einfügen. Der Code in dieser Funktion wird immer dann ausgeführt, wenn Sie eine beliebige Taste drücken:

void setup() {}

void draw() {}

void keyPressed() {
  println("Bravo! Eine Taste! Yeah!");
}

Datentyp char

Große Preisfrage: Welche Taste wurde gedrückt? Dazu müssen wir erstmal den Datentyp char kennenlernen (ein Datentyp ist sowas wie int und float). char steht für "character" (engl. für Schriftzeichen). Eine solche Variable enthält genau ein Zeichen (Buchstaben, Zahlen oder Zeichen wie , . # * etc.). Ein einzelnes Zeichen wird mit einfachen Anführungszeichen abgegrenzt . Hier einige Beispiele für char-Variablen.

char foo = 'a';
char boo = 'A'; // ist nicht gleich foo!
char nochnZeichen = '#';
char machMalnPunkt = '.';

Systemvariable key

Die Systemvariable key enthält immer die zuletzt gedrückte Taste. Wichtig: Wenn Sie 'a' drücken und die Taste loslassen, enthält key immer noch 'a'. Im folgenden Code ist das dennoch kein Problem, weil keyPressed() nur ein Mal aufgerufen wird, wenn Sie die Taste 'a' drücken:

void setup() {
}

void draw() {
}

void keyPressed() {
  if (key == 'a') {
    println("A ha.");
  }
}

Kontinuierliche Tastenabfrage (boolesche Variable keyPressed)

Problem mit dem obigen Code: Wenn Sie damit ein Spiel steuern wollen, reagiert Processing nicht, wie Sie möchten:

// Kreis-Steuerung: suboptimale Lösung
int x = 50;

void setup() {
}

void draw() {
  background(255);
  ellipse(x, 50, 20, 20); // Kreis malen
}

void keyPressed() {
  if (key == 'a') {
    x++; // Kreis nach rechts bewegen => es hakt leider
  }
}
Interaktives Feld:(erst auf das graue Feld klicken, dann 'a')

Auch hier kommt uns Processing zu Hilfe. Die Variable keyPressed ist eine boolesche Variable, d.h. sie enthält entweder den Wert true oder den Wert false . Diese Variable ist immer dann true , wenn eine Taste im gedrückten Zustand ist. Das können wir ausnutzen, um direkt im draw() unsere Tastaturabfrage zu machen:

int x = 50;

void setup() {
}

void draw() {
  background(200);
  ellipse(x, 50, 20, 20);

  // Abfrage direkt in draw() statt in Funktion
  if (keyPressed) {
    if (key == 'a') {
      x++; // Kreis nach rechts bewegen
    }
  }
}
Interaktives Feld:(erst auf das graue Feld klicken, dann 'a')

Warum ist die keyPressed-Abfrage überhaupt notwendig? Wenn Sie sie weglassen, bewegt sich der Kreis nach Drücken und Loslassen der Taste 'a' ständig weiter, da key immer die zuletzt gedrückte Taste enthält.

Variable keyCode

Häufig wollen Sie Bewegung an die Cursortasten binden. Diese Tasten lassen sich, ähnlich wie ENTER, BACKSPACE, ESCAPE etc. nicht einfach zwischen zwei Anführungszeichen schreiben. Deshalb verwendet man die Variable keyCode , die einen numerischen Code für jede Taste enthält. Wenn Sie den keyCode von den Cursortasten kennen, können Sie per Vergleich die Tasten abfragen:

int x = 50;

void setup() {
}

void draw() {
  background(200);
  ellipse(x, 50, 20, 20);

  if (keyPressed) {
    if (keyCode == RIGHT) {
      x++; // Kreis nach rechts
    }
    if (keyCode == LEFT) {
      x--; // Kreis nach links
    }
  }
}
Interaktives Feld:(erst auf das graue Feld klicken, dann Cursortasten)

Im Code sehen Sie einen Vergleich von keyCode (enthält eine Zahl, nämlich den Code der zuletzt gedrückten Taste) und der Variablen RIGHT, welche die Zahl enthält, die die linke Cursortaste repräsentiert.

Interaktion mit Maustasten

Bei den Maustasten verhält es sich ähnlich wie mit der Tastatur. Sie haben wieder zwei Möglichkeiten.

Möglichkeit 1: Funktion mousePressed() schreiben

Sie können die Funktion mousePressed() schreiben. Diese wird immer dann aufgerufen, wenn eine der Maustasten gedrückt wird:

void draw() {
}

void mousePressed() {
  println("hello");
}

Beachten Sie, dass Sie innerhalb des Grafikfensters klicken müssen.

Über die Systemvariable mouseButton können Sie unterscheiden, welche der Maustasten gedrückt wurde:

void draw() {
}

void mousePressed() {
  if (mouseButton == LEFT) {
    println("hello");
  }
  if (mouseButton == RIGHT) {
    println("again");
  }
  if (mouseButton == CENTER) {
    println("in the middle");
  }
}

Ähnlich wie keyCode enthält diese Variable eine Zahl. Diese Zahl können Sie mit einer der Systemvariablen LEFT, RIGHT, CENTER vergleichen, um zu prüfen, welche der drei Tasten gedrückt wurde.

Möglichkeit 2: boolesche Variable mousePressed in draw()

Analog zu keyPressed gibt es eine boolesche Variable mousePressed, die solange true ist, wie der Benutzer eine der Maustasten gedrückt hält. Das erlaubt eine kontinuierliche Abfrage innerhalb der draw()-Funktion wie hier:

void draw() {
  if (mousePressed) {
    if (mouseButton == LEFT) {
      println("hello");
    }
    if (mouseButton == RIGHT) {
      println("again");
    }
    if (mouseButton == CENTER) {
      println("in the middle");
    }
  }
}

Sie sehen, dass 60 Mal pro Sekunde die Ausgabe gemacht wird, sofern Sie eine der Tasten gedrückt halten.

Übungsaufgaben

Wir beginnen, das Spiel "Raincatcher" zu schreiben. Es geht darum, einen Eimer zu steuern, der Regentropfen einfängt.

Achten Sie darauf, Ihre Variablen "gut" zu benennen, damit Sie im weiteren Verlauf nicht den Überblick verlieren. Also besser eimerX statt ex oder x.

Das Auffangen der Tropfen können Sie im Aufgabenteil des nächsten Abschnitts finden.

(a) Eimer

Programmieren Sie einen "Eimer", den Sie mit den Cursortasten (links/rechts) steuern können. Achten Sie darauf, dass der Eimer flüssig nach links und rechts fährt, und dass er nie die Grenzen des Bildschirms verlässt.

(Sie müssen erst das Fenster anklicken, bevor Sie die Tasten verwenden können. Wenn Sie über dieser Zeile eine Lücke sehen, probieren Sie Firefox.)

(b) Tropfen

Programmieren Sie die Tropfen: Drei Tropfen (oder andere Gegenstände) fliegen von oben nach unten. Der x- und y-Startpunkt sind beide zufällig zu Beginn. Sobald ein Tropfen den Boden erreicht, verschwindet er und ein "neuer" Tropfen erscheint an zufälliger Position am oberen Rand, mit einer zufälligen neuen Farbe.

Vermeiden Sie es, dass Tropfen am Rand "angeschnitten" erscheinen.

Tipp: Beginnen Sie mit einem einzigen Tropfen.

(Sie müssen erst das Fenster anklicken, bevor Sie die Tasten verwenden können. Wenn Sie über dieser Zeile eine Lücke sehen, probieren Sie Firefox.)

Zusammenfassung

Sie haben gesehen, dass Sie prinzipiell zwei Möglichkeiten haben, eine Tastaturabfrage durchzuführen: (1) indem Sie eine eigene Funktion keyPressed() schreiben oder (2) indem Sie in draw() eine If-Anweisung einbauen, die auf die boolesche Variable keyPressed reagiert.

In beiden Fällen können Sie mit weiteren If-Anweisungen zwischen verschiedenen Tasten unterscheiden. Dabei verwenden Sie die Systemvariable key für die 'normalen' Tasten (a, b, c, ...) und die Variable keyCode für solche Tasten, die kein Zeichen auf dem Bildschirm produzieren (ESCAPE, ENTER, Cursortasten etc.).

Beachten Sie, dass die Variable key vom Typ char ist (nicht String), so dass Sie key == 'a' schreiben müssen (einfache Anführungszeichen).

Die Abfrage der Maustasten funktioniert analog zu der Tastaturabfrage. Sie verwenden die Funktion mousePressed(), wenn Sie nur einmal auf den Druck reagieren wollen. Sie verwenden die boolesche Variable mousePressed innerhalb von draw(), wenn Sie eine kontinuierliche Steuerung benötigen (z.B. Dauerfeuer in einem Shooter oder eine fließende Links-Rechts-Bewegung über Maustasten).

5.6 Anwendung: Buttons

In diesem Kapitel überlegen wir uns, wie einfach interaktive Elemente funktionieren, wie Sie sie für ein grafisches Interface (GUI, graphical user interface) benötigen würden.

Das einfachste interaktive Element ist eine Form, die "aktiv" wird, sobald der Mauszeiger auf ihr liegt, und "inaktiv", sobald der Mauszeiger wieder fort ist.

Dazu müssen wir zunächst wissen, wie wir feststellen, dass der Mauszeiger auf einem Objekt liegt. Das nennt man auch Kollisionerkennung und ist in Computerspielen von hoher Bedeutung.

Rechteck

Kollisionserkennung zwischen einem Punkt (Mauszeiger) und eine Rechteck, das parallel zu den Achsen des Koordinatensystems liegt, ist sehr einfach. Wenn der Punkt (x, y) ist und das rechteck über rx, ry, rwidth, rheight definiert ist, dann liegt eine Kollision vor, wenn die folgenden zwei Bedingungen gelten

rx <= x <= rx + rwidth
ry <= y <= ry + rheight

Im Code sieht das so aus (statt x, y finden Sie mouseX, mouseY):

// interface_1
// Kollision mit Rechteck (Selektion per Rollover)

int rx = 50;
int ry = 50;
int rwidth = 150; // Breite
int rheight = 200; // Höhe

void setup() {
  size(300, 300);
  noStroke();
}

void draw() {

  // setze Füllfarbe ...
  if (rx <= mouseX && mouseX <= rx + rwidth &&
    ry <= mouseY && mouseY <= ry + rheight) {
    // ... wenn Mauszeiger im Rechteck => rot
    fill(255, 0, 0);
  }
  else {
    // ... sonst: weiß
    fill(255);
  }
  rect(rx, ry, rwidth, rheight);
}

Interaktives Feld:(gehen Sie mit der Maus über das Rechteck!)

Kreis

Kollisionserkennung zwischen einem Punkt und einem Kreis ist sogar noch einfacher. Dazu vergleicht man die Distanz des Punkts (x, y) mit dem Mittelpunkt (cx, cy) des Kreises. Ist die Distanz geringer als der Radius, so ist der Punkt im Kreis:

dist(x, y, cx, cy) < radius

Die Funktion dist() berechnet den Abstand zweier Punkte. Im folgenden Code nehmen wieder mouseX, mouseY den Platz von x, y ein und der Radius errechnet sich aus diameter/2.

// interface_2
// Kollision mit Kreis (Selektion per Rollover)

int cx = 170;
int cy = 160;
int diameter = 180; // Durchmesser

void setup() {
  size(300, 300);
  noStroke();
}

void draw() {
  // setze Füllfarbe ...
  if (dist(mouseX, mouseY, cx, cy) < diameter/2) {
    // ... wenn Mauszeiger im Rechteck => rot
    fill(255, 0, 0);
  }
  else {
    // ... sonst: weiß
    fill(255);
  }
  ellipse(cx, cy, diameter, diameter);
}

Interaktives Feld:(gehen Sie mit der Maus über den Kreis!)

Übungsaufgaben

Zeichnen Sie die folgenden Figuren zunächst auf Papier und schreiben Sie dann die entsprechende Befehlsfolge auf.

(a) Farb-Buttons

Erstellen Sie drei weiße Buttons mit den Beschriftungen "Rot", "Grün" und "Blau".

Solange der Mauszeiger über einem der Buttons schwebt, soll der entsprechende Button hellgrau sein.

Wenn der User auf einen der Buttons klickt, soll der Hintergrund die entsprechende Farbe annehmen.

Wenn der User außerhalb der Buttons auf den Hintergrund klickt, wird dieser wieder grau.

(Wenn Sie über dieser Zeile eine Lücke sehen, probieren Sie Firefox.)

(b) Raincatcher

Jetzt können Sie Ihre Regentropfen einsammeln (siehe Übungsaufgaben oben)! Zählen Sie einen Punkt hoch, wenn Sie einen Topfen fangen. Zählen Sie ein "Leben" runter, wenn Ihnen ein Tropfen entwischt.

Beginnen Sie evtl. nur mit einem Tropfen. Das Programm wird bereits recht umfangreich. Achten Sie also auf gute Kommentierung und gute Benennung Ihrer Variablen.

(Sie müssen erst das Fenster anklicken, bevor Sie die Tasten verwenden können. Wenn Sie über dieser Zeile eine Lücke sehen, probieren Sie Firefox.)