Wir haben nun schon einige Formen des Programmablaufs kennengelernt. Der static Mode zum Beispiel, bei dem einfach nur linear eine Zeile nach der anderen abgearbeitet wird und zwar genau einmal. Und den active Mode, ein quasi endloser Modus, der zwar auch Zeile für Zeile abarbeitet, aber dies ständig wiederholt. Hier war es uns bereits möglich Interaktion einzubauen, indem wir zum Beispiel an der Stelle der Mauskoordinaten etwas gezeichnet haben. Der nächste Schritt Richtung Interaktion ist die ereignisgesteuerte Programmierung.
CallBack-Funktionen
Ereignisgesteuert bedeutet in diesem Fall, dass es Programmcode gibt, der nur dann ausgeführt wird, wenn ein bestimmtes Ereignis eintrifft - zum Beispiel, wenn der Nutzer eine bestimmte Taste auf der Tastatur drückt. Da dieser Programmcode nicht immer ausgeführt werden soll, steht dieser auch nicht in setup oder draw, sondern außerhalb. Beispiel:
void setup() {}
void draw() {}
void keyPressed() {
// Dieser Code wird durchgeführt, sobald eine Taste gedrückt wird
}
Die Funktion keyPressed wird hier als eine sogenannte CallBack-Funktion (auf deutsch = "Rückruf") bezeichnet. Als CallBack-Funktionen bezeichnet man Funktionen, die von anderen Funktionen aufgerufen werden. Da wir selber in unserem Code keyPressed niemals aufrufen, muss dies logischerweise an einer anderen Stelle geschehen. Dies übernimmt Processing für uns im Hintergrund und stellt uns einfach nur die Funktion zur Verfügung, die wir mit unserem Code füllen dürfen. Du kannst dir das in etwa so vorstellen:
- Nutzer drückt Taste → System erkennt: Taste gedrückt → Processing bekommt die Info: Taste gedrückt → Processing ruft keyPressed auf
CallBack-Funktionen kommen in der ereignisgesteuerten Programmierung häufig zum Einsatz, da mit ihnen auf die "Befehle" (oder auch die Eingabe) des Nutzers reagiert werden kann. Einige dieser Funktionen bei Processing hast du teilweise auch schon verwendet. In diesem Kapitel wollen wir noch einmal etwas tiefer in die Interaktion mit Tastatur und Maus einsteigen.
Interaktion mit Tasten
Nachdem wir die If-Anweisung kennengelernt haben, können wir wesentlich differenzierter mit Benutzereingabe umgehen als zuvor. Wir können z.B. unterscheiden, welche Taste gedrückt wurde. Du weißt, dass du auf einen Tastendruck reagieren kannst, indem du die Funktion keyPressed() einfügst. Der Code in dieser Funktion wird immer dann ausgeführt, wenn du eine beliebige Taste drückst:
void setup() {}
void draw() {}
void keyPressed() {
println("Bravo! Eine Taste! Yeah!");
}
Systemvariable key
Die Systemvariable key enthält immer die zuletzt gedrückte Taste. Wichtig: Wenn du 'a' drückst und die Taste loslässt, enthält key immer noch 'a'. Im folgenden Code ist das dennoch kein Problem, weil keyPressed() nur ein Mal aufgerufen wird, wenn du die Taste 'a' drückst:
void setup() {
}
void draw() {
}
void keyPressed() {
if (key == 'a') { // key ist vom Datentyp char, daher die einfachen Anführungszeichen '
println("A ha.");
}
}
Die Systemvariable key ist vom Datentyp char und kann somit immer nur genau ein Zeichen enhalten. Außerdem werden in Verbindung mit key auch die einfachen Anführungszeichen ' verwendet, anstatt die doppelten ".
Kontinuierliche Tastenabfrage (boolesche Variable keyPressed)
Das Problem mit dem obigen Code: Wenn du damit ein Spiel steuern willst, reagiert Processing nicht, wie du möchtest:
// 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
}
}
Probiere den Code in Processing aus!
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
}
}
}
Probiere den Code in Processing aus!
Warum ist die keyPressed-Abfrage überhaupt notwendig? Wenn du sie weglässt, 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 willst du 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 du den keyCode von den Cursortasten kennst, kannst du 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
}
}
}
Im Code siehst du 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. Du hast wieder zwei Möglichkeiten.
Möglichkeit 1: Funktion mousePressed() schreiben
Du kannst die Funktion mousePressed() schreiben. Diese CallBack-Funktion wird immer dann aufgerufen, wenn eine der Maustasten gedrückt wird:
void draw() {
}
void mousePressed() {
println("hello");
}
Beachte, dass du innerhalb des Grafikfensters klicken musst!
Über die Systemvariable mouseButton kannst du 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 kannst du 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");
}
}
}
Du siehst, dass 60 Mal pro Sekunde die Ausgabe gemacht wird, sofern du eine der Tasten gedrückt hältst.
Zusammenfassung
Du hast gesehen, dass du prinzipiell zwei Möglichkeiten hast, eine Tastaturabfrage durchzuführen:
- Indem du eine eigene Funktion keyPressed() schreibst oder
- Indem du in draw() eine If-Anweisung einbaust, die auf die boolesche Variable keyPressed reagiert.
In beiden Fällen kannst du mit weiteren If-Anweisungen zwischen verschiedenen Tasten unterscheiden. Dabei verwendet man 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.).
Beachte , dass die Variable key vom Typ char ist (nicht String), so dass du key == 'a' schreiben musst (einfache Anführungszeichen).
Die Abfrage der Maustasten funktioniert analog zu der Tastaturabfrage. Du verwendest die Funktion mousePressed(), wenn du nur einmal auf den Druck reagieren möchtest. Du verwendest die boolesche Variable mousePressed innerhalb von draw(), wenn du eine kontinuierliche Steuerung benötigst (z.B. Dauerfeuer in einem Shooter oder eine fließende Links-Rechts-Bewegung über Maustasten).