Behandelte Konzepte/Konstrukte: Kontrollfluss, Flussdiagramm, if, else, else if, UND, ODER, NICHT, boolean, char, keyPressed, keyCode

Lernziele

  • Sie können den Begriff Kontrollfluss erklären
  • Sie kennen den Aufbau einer If-Anweisung und den Begriff Code-Block
  • Sie können eine Bedingung für eine If-Anweisung anhand einer Problemstellung formulieren
  • Sie können den Kontrollfluss von If und If-Else mit Hilfe von Flussdiagrammen erklären
  • Sie können Fallunterscheidungen mit If und If-Else programmieren, sowohl für statische Probleme als auch für Animationen
  • Sie können den Kontrollfluss If-Else-If mit einem Flussdiagrammen erklären
  • Sie können Fallunterscheidungen mit If-Else-If programmieren
  • Sie können boolesche Ausdrücke mit AND/OR für konkrete Anwendungsfälle formulieren
  • Sie wissen, wie man boolesche Variablen in konkreten Anwendungsfällen nutzt
  • Sie können mit Hilfe von If-Anweisungen auf bestimmte Tasten reagieren, auch bei kontinuierlichen Szenarien
  • Sie können Kollisionen zwischen einem Punkt (z.B. Mausposition) mit einem Rechteck oder einem Kreis bestimmen

Voraussetzungen

Sie sollten sicher mit Variablen umgehen können Kapitel 2.

Neueste Aktualisierungen (zuletzt 09.01.2023)
  • 09.01.2024: Level zu Aufgaben hinzugefügt
  • 15.01.2023: Motivation oben und Aufgabenverlinkung
  • 27.12.2021: Neue Aufgabe "Goldener Schnitt" in 3.1
  • 21.11.2021: Neue Aufgabe "Endless Landscape" in 3.1, neuer Website-Link und drei neue Abb. bei 3.6 Kollision
  • 06.11.2021: Neue Aufgabe in 3.6 "Drag and drop"
  • 02.11.2021: Kleinere Korrekturen...
  • 03.10.2021: Lernziele angepasst
  • 02.08.2021: Neue Kapitelnummerierung

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.

Oben sehen Sie zwei Beispiele. Im linken Fenster sehen Sie, wie man mit Hilfe einer If-Anweisung den Ball abhängig von seiner Position einfärben kann (Aufgabe 3.3e). Das rechte Fenster müssen Sie erst anklicken, dann können Sie das Raincatcher-Spiel spielen. In diesem Kapitel lernen Sie, wie man es programmiert (Aufgaben 3.5a, 3.5b, 3.6c).

3.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.

Flussdiagramm

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).

Man sieht in dem Diagramm sehr schön, dass der Kasten mit "bestanden" nicht immer ausgeführt wird, da sich der Kontrollfluss an der Raute aufteilt.

Wir erweitern unseren Code um eine Zeile hinter der If-Anweisung:

int punkte = 75;

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

Wir erweitern unser Flussdiagramm entsprechend, um zu klären, wann diese neue Codezeile ausgeführt wird.

Ablaufplan If-Anweisung mit weiterer Anweisung

Wir sehen, dass "tschüs" immer ausgegeben wird, 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 Bedingung 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".

Beispiel: Fliegender Ball

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 setup() {
  size(200, 100);
}

void draw() {
  background(0);
  ellipse(x, height/2, 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, bei unserem 200x100-Fenster also:

x == 200

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

Noch besser ist es, diese Bedingung 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, height/2, 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. Statt einer konstanten Zahl wie 1 wird der Wert dieser Variable auf das x addiert. Wir nehmen hier mal Geschwindigkeit 3:

int x = 0;
int speed = 3;

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

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

Plötztlich funktioniert das Neu-Erscheinen des Balls nicht mehr, er verschwindet einfach! Warum? Schauen wir uns mal an, welche Werte das x annimmt, indem wir uns x 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. Man sagt auch, dass man die Bedingung relaxiert, also etwas "weicher" fasst.

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

Wenn Sie die Geschwindigkeit mit einer negativen Zahl belegen, 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 zur Bedingung x < 0 und "rechts wiederauftauchen" realisiert man mit der Anweisung x = width.

Coding Style

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

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

Alternativ kann man einen Umbruch nach der Bedingung einfügen:

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

a) Größer 50

Gegeben sei eine int-Variable foo. Wenn diese größer als 50 ist, soll "OK" ausgegeben werden. Nehmen Sie verschiedene Werte für foo, um Ihr Programm zu testen.

Lösung
int foo = 60;

if (foo > 50) {
  println("OK");
}

b) a gewinnt

Gegeben seien zwei int-Variablen a und b. Wenn a um mindestens 10 größer ist als b, soll "a gewinnt" ausgegeben werden. Nehmen Sie verschiedene Werte für a und b, um Ihr Programm zu testen.

Lösung
int a = 50;
int b = 20;

if (a - 10 >= b) {
  println("a gewinnt");
}

c) Summe gewinnt

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 "Team ab gewinnt" ausgegeben werden.

Lösung
int a = 5;
int b = 2;
int c = 5;
int d = 0;

if (a + b > c + d) {
  println("Team ab gewinnt");
}

d) If-Else

Gegeben seien zwei int-Variablen z1 und z2. Wenn die zwei Zahlen gleich sind, soll "gleich" ausgegeben werden, sonst "ungleich".

Lösen Sie die obige Aufgabe, indem Sie im ersten If-Teil "ungleich" ausgeben und im Else-Teil "gleich". Passen Sie dazu die Bedingung an.

Lösung
int z1 = 5;
int z2 = 11;

if (z1 == z2) {
  println("gleich");
} else {
  println("ungleich");
}
Für die zweite Variante:
if (z1 != z2) {
  println("ungleich");
} else {
  println("gleich");
}

Übungsaufgaben

3.1 a) Gerade/ungerade   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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. Schauen im Kapitel "Variablen" unter "Operatoren" nach, wenn Sie vergessen haben, wie Modulo funktioniert.

3.1 b) Verschachteltes If   Level 11 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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.

3.1 c) Klausurnoten   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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.

3.1 d) Stehenbleiben   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Ändern Sie das folgende Programm derart, dass der Ball stehenbleibt, sobald er die Mitte erreicht. Das heißt, der Ball bewegt sich bis zur Mitte und bleibt dann dort.

int x = 0;

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

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

3.1 e) Hoch-runter   Level 11 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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

3.1 f) Wachsen pro Durchlauf   Level 11 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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

3.1 g) Wiederholtes Wachsen   Level 11 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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.

3.1 h) Mutierender Mauszeiger   Level 11 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Folgendes Programm erzeugt einen Mauszeiger in Form eines Kreises. Verändern Sie das Programm derart, dass in der rechten Bildschirmhälfte 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);
}

3.1 i) Timer   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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;
Tipp
Zählen Sie den Zähler in der jeder Runde um 1 hoch und testen Sie, ob die FrameRate überschritten ist. Dann geben Sie den Punkt aus und setzen den Zähler zurück.

Hinweis 1: Je nach Rechner und aktueller Rechnerbelastung wird 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 2: Nutzen Sie nicht die Funktion frameRate(), sondern die Systemvariable frameRate wie oben angegeben.

3.1 j) Glücksspiel   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Sie möchten ein Glücksspiel realisieren, wo bei jedem Tastendruck mit einer bestimmten Wahrscheinlichkeit (in Prozent) ein "gewonnen" ausgegeben wird (sonst "verloren").

Ihr Programm könnte so aussehen:

int gewinnChance = 20;

void draw() {}

void keyPressed() {
  // Ihr Code
}

Testen Sie Ihr Programm, indem Sie 10x eine Taste drücken und schauen, ob der Anteil der gewonnenen Runden ungefähr Ihrer Angabe in gewinnChance entspricht.

Tipp
Verwenden Sie die Funktion random(), um eine Zahl zwischen 0 und 100 zu generieren. Das ist sozusagen Ihr "Würfel".

3.1 k) Endless Landscape   Level 41 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Animieren Sie ein "Gebirge", das von rechts nach links zieht.

Tipp
Verwenden Sie drei Koordinaten (jeweils x und y), um immer zwei Linien zu zeichnen (= den aktuell sichtbaren Berg). Die Punkte sind immer so weit auseinander, wie der Bildschirm breit ist. Wenn der zweite Punkt links verschwindet, müssen Sie die Punkte ändern (u.a. einen neuen dritten Punkt erschaffen).

Wenn Sie das obige Gebirge geschafft haben, fügen Sie einfach noch einen Punkt hinzu.

3.1 l) Stoppuhr   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Programmieren Sie eine Art Stoppuhr-Anzeige, die immer wieder von vorn beginnt.

Tipp
Nutzen Sie die Funktion arc(), die ein Kreissegment zeichnet. Denken Sie daran, dass der erste Winkel kleiner als der zweite sein muss. Sie können auch negative Winkel verwenden.

3.1 m) Goldener Schnitt   Level 51 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Wir betrachten eine Strecke c und zerlegen sie in die zwei Teilstrecken a und b. Wir nehmen mal an, dass a die längere der beiden Teilstrecken ist und b die kürzere.

Jetzt kann man sich bestimmte Längenverhältnisse anschauen. Das eine Verhältnis ist a:b, also das Verhältnis der zwei Teilstrecken zueinander, genauer gesagt, das Verhältnis der langen Teilstrecke a zur kurzen Teilstrecke b. Das andere Verhältnis ist c:a, also das Verhältnis der gesamten Strecke c zu der längeren Teilstrecke a. Diese Verhältnisse berechnet man einfach per Division - etwa c/a - und erhält so eine Dezimalzahl. Ist die Strecke c eineinhalb Mal so lang wie a, dann ist das Verhältnis c:a also 1.5.

Der Goldene Schnitt bezieht sich auf eine ganz bestimmte Teilung, nämlich diejenige Teilung, die erreicht, dass beide Verhältnisse genau gleich sind, also c:a = a:b. In dem verlinkten Wikipedia-Artikel finden Sie den genauen Wert.

Programmieren Sie eine Visualisierung des Goldenen Schnitts, bei der man die Strecke c sieht, sowie die Teilstrecken a und b. Mit der x-Position der Maus kontrolliert man die Teilung von c. Aufgabe ist es, die Länge von a so zu bestimmen, dass sich der Goldene Schnitt ergibt. Ob der Goldene Schnitt erreicht ist, prüfen Sie, indem Sie die Verhältnisse c:a und a:b vergleichen. Probieren Sie es unten aus und programmieren Sie es entsprechend.

Um zu bestimmen, ob zwei Float-Zahlen "gleich" sind, betrachten Sie den Betrag der Differenz zweier Float-Werte. Den Betrag bekommen Sie mit der Funktion abs. Das Programm soll bis zur zweiten Nachkommastelle genau sein.

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.

3.2 If-Else-If

Video: Else if (9:24)

Wenn Sie größere Fallunterscheidungen haben, bietet sich das If-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:

int punkte = 55;

// äußere If-Anweisung

if (punkte > 50) {

  // innere If-Anweisung

  if (punkte > 75) {
    println("Bestnote!");
  } else {
    println("Bestanden.");
  }

} else {
  println("Leider durchgefallen"); // Else-Teil
}

Die äußere If-Anweisung unterscheidet zwischen >50 und <=50. Die innere If-Anweisung teilt den Bereich >50 dann nochmal in >75 und <=75.

Auch wenn Verschachtelung erlaubt und in manchen Fällen auch sinnvoll ist, so ist dieser Code doch schwer zu lesen und je mehr Fälle Sie haben, umso unübersichtlicher 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. Falls 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).

Fingerübungen

a) Positiv/negativ

Gegeben sei eine int-Variable zahl. Wenn diese größer als oder gleich 0 ist, soll "positv" ausgegeben werden. Wenn sie kleiner als 0 ist, soll "negativ" ausgegeben werden. Probieren Sie verschiedene Werte für zahl, um Ihr Programm zu testen.

Lösung
int zahl = -5;

if (zahl >= 0) {
  println("positiv");
} else {
  println("negativ");
}

Übungsaufgaben

3.2 a) Klausurnoten   Level 11 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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

int punkte = 10;

Schreiben Sie 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.

3.2 b) Lauftext   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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.

3.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 Beispiel 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

Beispiel: Abprallender Ball

Wir 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;
}

Vorzeichen umdrehen

Wenn wir die Lösung allgemeiner betrachten, sehen wir, dass in beiden Fällen einfach das Vorzeichen der Geschwindigkeit 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, ob 1, 15 oder 3002. 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;
  }
}

Zusammenfassen der If-Anweisungen

In dem oberen Code fällt auf, dass in beiden If-Anweisungen der exakt gleiche Code ausgeführt wird. Man könnte also auch sagen: wenn x > width oder x < 0 der Fall ist, dann drehe das Vorzeichen um. Also ein Fall für den booleschen Operator ODER:

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:

Sie können diesen Code relativ leicht auf zwei Dimensionen erweitern. Fügen Sie einfach entsprechende Variablen für die y-Richtung hinzu.

Visualisieren der Geschwindigkeit

Wir können die Geschwindigkeit als Linie zeichnen. So können wir sowohl die Größenordnung als auch die Richtung ablesen.

Wir zeichnen eine (rote) Linie vom Mittelpunkt des Balls und nehmen das zehnfache der Geschwindigkeit als Länge (sonst wäre die Linie nur 3 Pixel lang).

int x = 0;
int speed = 3;

void setup() {
  size(200, 100);
  strokeWeight(2);
}

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

  stroke(255, 0, 0);
  line(x, 50, x + 10*speed, 50);

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

Probieren Sie den Code auch mit anderen Geschwindigkeiten.

Fingerübungen

a) Enthalten 1

Erstellen Sie drei float-Variablen a, b und x. Gehen Sie davon aus, dass a immer kleiner als b ist.

Schreiben Sie "x innerhalb" auf die Konsole, wenn x zwischen a und b liegt (die Werte a, b sollen dabei ausgeschlossen sein), sonst "x außerhalb". Testen Sie Ihr Programm mit entsprechenden Werten.

Lösung
float a = 23;
float b = 110;
float x = 55;

if (x > a && x < b) {
  println("x innerhalb");
} else {
  println("x außerhalb");
}

b) Enthalten 2

Erweitern Sie obiges Programm so, dass es auch funktioniert, wenn b kleiner als a ist.

Lösung
float a = 110;
float b = 23;
float x = 55;

if (x > a && x < b || x > b && x < a) {
  println("x innerhalb");
} else {
  println("x außerhalb");
}

c) Extrem

Erstellen Sie drei float-Variablen a, b und x. Gehen Sie davon aus, dass a immer kleiner als b ist.

Schreiben Sie "x extrem" auf die Konsole, wenn x außerhalb des Intervalls a und b liegt. Wenn x innerhalb des Intervalls liegt, passiert nichts. Verwenden Sie ein einfaches If (ohne Else-Teil).

Lösung
float a = 23;
float b = 110;
float x = 250;

if (x < a || x > b) {
  println("x extrem");
}

Übungsaufgaben

3.3 a) Human Resources   Level 11 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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.

// Ändern Sie die Werte zum Testen
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).

3.3 b) Human Resources 2   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Sie sind Personaler bei einer Softwarefirma. Sie möchten die Sichtung der Bewerbungen automatisieren. Sie schauen sich die Abschlussnote (z.B. 1,7 oder 2,3) an und die Programmiererfahrung (zwischen 1 für "keine" und 5 für "hohe").

  • Bewerber mit der Note 1 werden direkt eingestellt. Bewerber mit Programmiererfahrung der Stufe 5 werden auch direkt eingestellt, sofern sie mindestens die Note 3 haben.
  • Bewerber mit Note 2 werden zum Gespräch eingeladen. Wenn Programmiererfahrung der Stufe 4 vorhanden ist, werden sie auch - unabhängig von der Note - eingeladen.
  • Alle anderen werden abgelehnt

Schreiben Sie Code, der "einstellen" oder "zum Gespräch einladen" oder "ablehnen" ausgibt. Nutzen Sie die folgenden Variablen, deren Werte Sie natürlich mehrfach ändern müssen, um die Korrektheit Ihres Codes zu testen.

// Ändern Sie die Werte zum Testen
float note = 2.3;
int programmiererfahrung = 5;
Tipp
Nutzen Sie `Else-If` für Ihre Fallunterscheidung.

3.3 c) Pulsieren   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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. Wenn Sie diesen Hinweis ignorieren, kann es sein, dass Ihr Programm nur aufgrund eines Artefakts "funktioniert", aber eigentlich falsch ist (und z.B. bei zukünftigen Versionen von Processing nicht mehr funktioniert).

3.3 d) Pulsierende Farbe   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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".

3.3 e) Rote Zone   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Verwenden Sie folgenden Basiscode und verändern Sie ihn so, dass der Ball in der markierten Zone rot wird.

int x = 0;

void draw() {
  background(0);

  // Linien zum Markieren der Zone
  stroke(255);
  line(25, 0, 25, height);
  line(75, 0, 75, height);

  // hier wird animiert...
  ellipse(x, 50, 20, 20);
  x++;
  if (x > width) {
    x = 0;
  }
}
Tipp
Überlegen Sie, was genau Sie mit einem If ändern: Es geht nur um die Farbe, rot oder weiß.

3.3 f) Rote Zone invers   Level 11 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Verändern Sie den Code aus der obigen Aufgabe so, dass der Ball außerhalb der markierten Zone rot wird.

Wichtig: Lösen Sie diese Aufgabe, indem Sie die Bedingung ändern.

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.

3.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 funktioniert, 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 setzen 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);

Fingerübungen

a) Richtig/falsch

Gegeben sei eine boolesche Variable istKorrekt. Wenn diese wahr ist, soll "richtig" ausgegeben werden, sonst "falsch".

Testen Sie Ihr Programm mit beiden möglichen Werten.

Lösung
boolean istKorrekt = true;

if (istKorrekt) {
  println("richtig");
} else {
  println("falsch");
}

b) Vergeich

Gegeben seien zwei boolesche Variablen foo und bar. Geben Sie "gleich" aus, wenn bei Variablen gleich sind, sonst "ungleich".

Lösung
boolean foo = true;
boolean bar = false;

if (foo == bar) {
  println("gleich");
} else {
  println("ungleich");
}

c) Größer/kleiner

Erstellen Sie drei Variablen a (int), b (int) und agb (boolean). Setzen Sie a und b auf beliebigen Werte.

Die Variable agb soll true enthalten, wenn a größer b ist. Geben Sie agb auf der Konsole aus. Testen Sie Ihr Programm mit verschiedenen Werten für a und b.

Lösung
int a = 5;
int b = 10;

boolean agb = a > b;

println(agb);

Übungsaufgaben

3.4 a) Umschalten   Level 11 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Kopieren Sie den folgenden Code. Sie dürfen nur die zwei Kommentare durch eigenen Code ersetzen!

boolean foo = true;

// Hier kommt Ihr Code hin

println(foo);

// Gleicher Code wie oben

println(foo);

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

Als Ausgabe sollten Sie sehen:

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.

3.4 b) Now you see me...   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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.

Wichtig: Lösen Sie diese Aufgabe mit Hilfe einer booleschen Variable.

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.

3.4 c) Kreis oder Box   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Wo immer der 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.

3.4 d) horizontal oder vertikal   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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)

3.4 e) Fly-by   Level 41 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Programmieren Sie das folgende interaktive Element. Es handelt sich um eine Box, die nach außen schwebt, sobald man mit der Maus klickt (egal wohin).

Für die Animation können Sie die Systemvariable frameCount verwenden. Mit einer booleschen Variablen können Sie markieren, dass die Animation gerade läuft (oder eben nicht). Speichern Sie die Geschwindigkeit der Animation in einer Speed-Variablen, die Sie nach jeder Animation wieder "umdrehen".

Die abgerundeten Ecken bekommen Sie mit einem fünften Paramter bei rect. Dieser ist der Radius der Abrundung (z.B. 10 Pixel).

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++;
}

3.5 Tasten unterscheiden

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.

3.5 a) Eimer   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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.)

3.5 b) Tropfen   Level 41 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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.)

3.5 c) Gate   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Programmieren Sie ein "Tor", das Sie mit den Cursortasten (hoch/runter) steuern können. Achten Sie darauf, dass das Tor nicht noch oben/unten verschwinden kann.

(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).

3.6 Kollisionserkennung

In diesem Kapitel überlegen wir uns, wie einfach interaktive Elemente funktionieren, wie Sie sie für ein grafisches Interface (GUI, graphical user interface) und Spiele 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.

Punkt-Rechteck

Kollisionserkennung zwischen einem Punkt (z.B. dem Mauszeiger) und einem Rechteck, das parallel zu den Achsen des Koordinatensystems liegt, ist sehr einfach.

Als Rechteck nehmen wir ein Rechteck mit Eckpunkt (rx, ry) und der Größe rwidth und rheight. Den Punkt, für den wir die Kollision berechnen wollen, nennen wir (x, y).

Kollision zwischen einem Punkt und einem Rechteck

Wir überlegen uns das Problem zunächst nur für die x-Achse. Wenn wir nur die x-Achse betrachten, heißt "Kollision", dass der Wert x innerhalb des Bereichs rx und rx + rwidth liegt. Wenn das der Fall ist, liegt unser Punkt also schonmal innerhalb des korrekten x-Bereichs.

Projektion auf die x-Achse

Anschließend betrachten wir die y-Achse. Auch da gilt: "Kollision" heißt hier, dass der y-Wert des Punkts zwischen den Werten ry und ry + height liegt.

Insgesamt schließen wir also: Der Punkt liegt im Rechteck, wenn die folgenden zwei Bedingungen gleichzeitig 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!)

Punkt-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.

Kollision zwischen einem Punkt und einem Kreis

Ist die Distanz geringer als der Radius, so ist der Punkt im Kreis:

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

Die Processing-Funktion dist() berechnet den Abstand zweier Punkte, die mit insgesamt vier Parametern angegeben werden. 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!)

Weitere Kollisionen

Mit den obigen Techniken können Sie also Kollisionen zwischen Punkt-Rechteck und Punkt-Kreis erkennen. Damit lässt sich schon eine Menge anfangen.

Wer aber auch wissen will, wie man Kollisionen zwischen

  • Rechteck-Rechteck
  • Kreis-Rechteck
  • Punkt-Linie
  • Linie-Linie
  • etc.

erkennen kann, kann sich die folgende Webseite mit interaktiven Processing-Beispielen ansehen: Collision Detection by Jeff Thompson.

Als Beispiel die Kollision Rechteck-Rechteck (siehe Thompsons Collision Rectangle-Rectangle).

Interaktives Feld:

Übungsaufgaben

3.6 a) Farb-Buttons   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

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.)

3.6 b) Überlappende Buttons   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Sie haben zwei Quadrate mit Seitenlänge 50px. Färben Sie den "Mauszeiger" (Kreis) farbig ein, sobald er innerhalb der Bereiche ist. Konkret: rot im ersten Quadrat, grün im zweiten Quadrat und blau in der Schnittmenge.

Verwenden Sie den Basiscode und versuchen Sie, den Code so zu schreiben, dass das Programm auch funktioniert, wenn man die Werte von x1, y1, x2, y2 ändert.

Interaktives Feld:

Basiscode:

int x1 = 30;
int y1 = 10;
int x2 = 10;
int y2 = 40;

void draw() {
  background(255);
  noFill();
  rect(x1, y1, 50, 50);
  rect(x2, y2, 50, 50);

  ellipse(mouseX, mouseY, 20, 20);
}
Tipp
Auch hier gilt: Teile und herrsche (divide and conquer). Das Problem ist die Schnittmenge. Schauen Sie sich diesen Schnittbereich zunächst nur auf der x-Achse an. Können Sie z.B. mit den Funktionen min und max die Bedingung für das entsprechende If aufstellen? (Schauen Sie in der Processing-Referenz nach diesen Funktionen...)

3.6 c) Raincatcher   Level 51 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Jetzt können Sie Ihre Regentropfen einsammeln (siehe Aufgaben 3.5a Eimer und 3.5b Tropfen)! 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.)

3.6 d) Kollision Punkt-Linie   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Gegeben sind ein Punkt a und eine Linie b. Die Linie können Sie mit den Pfeiltasten bewegen.

Ergänzen Sie den Basiscode unten so, dass immer wenn der Punkt "innerhalb" der Linie liegt, die Linie rot gefärbt erscheint.

(Sie müssen erst das Fenster anklicken, bevor Sie die Tasten verwenden können.)

Hier ist der Basiscode. Konzentrieren Sie sich ganz bewusst auf die x-Werte.

float ax = 50; // x-Position von Punkt a

float bx = 5; // x-Position des Startpunkts der Linie b
float bwidth = 30; // Breite der Linie b

void draw() {
  background(0);
  strokeWeight(5);

  stroke(0,255,0);
  point(ax, 50);

  stroke(255);

  // HIER IHR CODE

  line(bx, 60, bx+bwidth, 60);

  if (keyPressed) {
    if (keyCode == LEFT) {
      bx -= 2;
    } else if (keyCode == RIGHT) {
      bx += 2;
    }
  }
}

3.6 e) Kollision Linie-Linie   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Wir erweitern das Szenario aus der letzten Aufgabe so, dass a jetzt auch eine Linie ist. Wie müssen Sie den Code erweitern, damit die untere Linie b immer dann rot wird, wenn sich die beiden Linien überschneiden?

(Sie müssen erst das Fenster anklicken, bevor Sie die Tasten verwenden können.)

Und wieder Basiscode:

float ax = 60; // x-Position des Startpunkts der Linie a
float awidth = 30; // Breite der Linie a

float bx = 5; // x-Position des Startpunkts der Linie b
float bwidth = 40; // Breite der Linie b

void setup() {
  size(150, 100);
}

void draw() {
  background(0);
  strokeWeight(4);

  stroke(0,255,0);
  line(ax, 50, ax+awidth, 50);

  stroke(255);

  // HIER IHR CODE

  line(bx, 60, bx+bwidth, 60);

  if (keyPressed) {
    if (keyCode == LEFT) {
      bx -= 2;
    } else if (keyCode == RIGHT) {
      bx += 2;
    }
  }
}

3.6 f) Pong   Level 41 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Programmieren Sie den Spiele-Klassiker Pong.

Hier sehen Sie die Ein-Spieler-Version, wo man versuchen muss, den Ball nicht auf den Boden aufkommen zu lassen.

(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.)

Tipps: Um die Kollision zwischen "Ball" und dem Balken zu berechnen, helfen Ihnen vielleicht die zwei vorigen Aufgaben. Um den Zustand "Game Over" vom eigentlichen Spiel zu trennen, verwenden Sie eine boolesche Variable gameOver, die anfangs false ist. In draw() packen Sie dann gleich zu Beginn ein if (gameOver) ..., wo entweder der Schriftzug "Press SPACE" gezeigt wird oder (im Else-Teil) das Spiel verarbeitet wird.

3.6 g) GateGame   Level 41 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Programmieren Sie das folgende Spiel: Ein Objekt kommt wiederholt von links nach rechts geflogen (die Höhe ist zufällig). Der Spieler steuert ein Tor. Wenn das Objekt durch das Tor fliegt, bekommt der Spieler einen Punkt. Wenn das Objekt nicht das Tor trifft, leuchtet es rot und der Spieler verliert einen Punkt.

Mit der Leertaste setzt man den Spielstand auf Null.

(Sie müssen erst das Fenster anklicken, bevor Sie die Tasten verwenden können.)

3.6 h) Drag and drop   Level 51 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Sie sollen "Drag and drop" realisieren, d.h. wenn die Maus über dem Kreis ist, kann man den Kreis bewegen, indem man die Maus bei gedrückter Taste bewegt.

Probieren Sie zunächst die einfache Variante. Hier springt der Kreis, sobald Sie mit der Maus über dem Kreis sind und Sie die Maustaste drücken, mit seinem Mittelpunkt zur Maus.

Bei dem folgenden Programm wird der Kreis rot eingefärbt, sobald der Mauszeiger über dem Kreis ist. Der gelbe Rand erscheint, wenn zusätzlich die Maustaste gedrückt wird. Verzichten Sie gern auf den Rand und konzentrieren Sie sich auf das Drag.

Dass der Kreis immer mit dem Mittelpunkt zur Maus springt ist gerade bei größeren Objekten nicht schön. Jetzt bewegen Sie den Kreis so, dass er sich immer relativ zur Mauszeigerbeweung bewegt. Verwenden Sie dazu zum Beispiel pmouseX und pmouseY, das ist die Mausposition des vorhergehen Frames.

3.6 i) Fly-by II   Level 51 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Greifen Sie die Aufgabe Fly-by nochmal auf. Jetzt soll nur das Element ausgefahren werden, auf dem sich die Maus befindet.