Thema: Regenfänger

Beim Regenfänger handelt es sich um ein Spiel, bei dem Regentropfen vom "Himmel" fallen und man diese mit seiner Platform fangen muss. Die Regentropfen fallen zufällig in einem bestimmten Höhenbereich. Für jeden Tropfen den man fängt, erhält man Punkte und für jeden Tropfen den man nicht fängt verliert man Leben.

Doch bis zum fertigen Spiel, kommen einige Zeilen Code und diese wollen wir nun in Angriff nehmen!

Zunächsteinmal wird sich überlegt, was umgesetzt werden muss. Die obere Beschreibung des Spiels gibt uns dafür schon einige Hinweise:

- Regentropfen fallen vom Himmel
- Platform als Fänger
- Punkte für Tropfen
- Verlust von Leben für nicht gefangene Tropfen
- Tropfen fallen aus zufälliger Höhe

Wie man schon erkennen kann, ist einiges von den Regentropfen abhängig. Daher starten wir mit der Platform die als Fänger fungiert. An die Platform werden folgende "indirekte" Anforderungen gestellt:

- Kann sich nach links und rechts bewegen
- Bewegung der Platform wird eingegrenzt durch das vorhandene Spielfeld
- Befindet sich auf einer festen Höhe

Mit diesen Informationen können wir unsere erste Konzeptzeichnung anlegen.


Wie man erkennt, hat unsere Platform eine Breite pWidth und eine Höhe pHeight. Die Position in y-Richtung befindet sich auf height - pHeight wobei height eine von Processing definierte Variable für die Höhe des Fensters ist. Wir möchten außerdem, dass unsere Platform zu Beginn mittig positioniert ist. Daher rechnen wir 0.5*width - 0.5*pWidth wobei width ebenfalls eine von Processing definierte Variable für die Breite des Fensters ist.

                int pHeight;
                int pWidth;

                void setup(){
                  pHeight = 20;
                  pWidth  = 20;
                }

                void draw(){
                  rect(0.5*width-0.5*pWidth, height-pHeight, pWidth, pHeight);
                }
                 
                


So ganz statisch hilft uns die Platform allerdings noch nicht um irgendwas zu fangen. Daher fügen wir nun die benötigte Bewegung hinzu. Dazu müssen wir die x-Richtung verändern. Dies geschieht am Besten nach einem Tastendruck durch den Benutzer.

                int pHeight;
                int pWidth;
                int x;

                void setup(){
                  background(255);
                  pHeight = 20;
                  pWidth  = 20;
                  x = 0;
                }

                void draw(){
                  background(255);
                  if(keyPressed){
                    if(keyCode == LEFT){
                      x--;
                    } else if(keyCode == RIGHT){
                      x++;
                    }

                  }
                  rect(0.5*width-0.5*pWidth+x, height-pHeight, pWidth, pHeight);
                }
                 
                

Die Variable x wird verwendet, um die Position der Platform zu modifizieren. Der Befehl background(255) ist lediglich da, um das Spielfeld in void draw() neu zu zeichnen. Wer möchte kann auch mal gucken was passiert, wenn man den Befehl weglässt.


Zwar haben wir nun die Bewegung implementiert, doch bewegen wir uns noch etwas zu weit. Hier kommt unsere Vorgabe "Bewegung der Platform wird eingegrenzt durch das vorhandene Spielfeld" zum Tragen.


Wir müssen prüfen, ob wir uns im Bereich 0 < (x,y) < width - pWidth befinden.

                int pHeight;
                int pWidth;
                float posX;
                int x;

                void setup(){
                  background(255);
                  pHeight = 20;
                  pWidth  = 20;
                  x = 0;
                  posX = 0;
                }

                void draw(){
                  background(255);
                  if(keyPressed){
                    if(keyCode == LEFT && 0 <= posX){
                      x--;
                    } else if(keyCode == RIGHT && posX <= width-pWidth){
                      x++;
                    }

                  }
                  posX = 0.5*width-0.5*pWidth+x;
                  rect(posX , height-pHeight, pWidth, pHeight);
                }
                 
                

Die Position auf der x-Achse haben wir zusätzlich in eine Variable ausgelagert, da wir diese Position brauchen um zu überprüfen ob sich unsere Platform in einem gültigen Bereich befindet.
Damit wären wir mit der Platform fertig. Noch ein kleiner Tipp am Rande, man sollte in diesem Fall nicht die Funktion void keyPressed() verwenden!

                int pHeight;
                int pWidth;
                float posX;
                int x;

                void setup(){
                  background(255);
                  pHeight = 20;
                  pWidth  = 20;
                  x = 0;
                  posX = 0;
                }

                void draw(){
                  background(255);
                  posX = 0.5*width-0.5*pWidth+x;
                  rect(posX , height-pHeight, pWidth, pHeight);
                }

                void keyPressed(){
                  if(keyCode == LEFT && 0 <= posX){
                    x--;
                  } else if(keyCode == RIGHT && posX <= width-pWidth){
                    x++;
                  }
                }
                 
                

Es führt zwar zu dem gleichen Ergebnis, allerdings erhaltet ihr keine saubere Steuerung eurer Platform. Wer es nicht glaubt, am Besten selbst ausprobieren!
Als nächstes beschäftigen wir uns mit den Regentropfen.


Die Regentropfen sollen auf einer beliebigen Höhe und Breite des Spielfelds erscheinen. Wir grenzen die Höhe allerdings ein wenig ein, um es für den Spieler nicht all zu schwierig zu gestalten.

Beliebige Breite: int(random(0, width))
Spezifische Höhe: int(random(0, 0.25*height))

Den Durchmesser legen wir mit 20 fest. Dadurch ergibt sich: arc(int(random(0, width)), int(random(0, 0.25*height)), 20, 20, 0, 2*PI)

                int pHeight;
                int pWidth;
                float posX;
                int x;
                float rx;
                float ry;

                void setup(){
                  background(255);
                  pHeight = 20;
                  pWidth  = 20;
                  x = 0;
                  posX = 0;
                  rx = random(0, width);
                  ry = random(0, 0.25*height);
                }

                void draw(){
                  background(255);
                  if(keyPressed){
                    if(keyCode == LEFT && 0 <= posX){
                      x--;
                    } else if(keyCode == RIGHT && posX <= width-pWidth){
                      x++;
                    }

                  }
                  posX = 0.5*width-0.5*pWidth+x;
                  rect(posX , height-pHeight, pWidth, pHeight);
                  arc(rx, ry, 20, 20, 0, 2*PI);
                }
                 
                

Die Positionen wurden wieder ausgelagert in Variablen, da nicht bei jedem Zeichendurchlauf der Tropfen "zufällig" gezeichnet werden soll. Hier haben wir allerdings auch wieder das Problem, dass sich die Regentropfen zur Hälfte in der Wand verstecken können. Dies lässt sich aber leicht beheben.

Dazu einfach rx und ry folgenderweise anpassen:

rx = int(random(10, width-10));
ry = int(random(10, 0.25*height));

Dabei handelt es sich um die Hälfte der Größe des Kreises 0.5 * 20 = 10.


Damit der Regentropfen fällt, muss die Variable ry erhöht werden. Sobald der Tropfen im Boden verschwindet, soll er neu gezeichnet werden. Zudem fügen wir noch eine Variable "life" hinzu die für jeden Fall durch den Boden reduziert wird.

                int life;
                int pHeight;
                int pWidth;
                float posX;
                int x;
                float rx;
                float ry;

                void setup(){
                  background(255);
                  life = 10;
                  pHeight = 20;
                  pWidth  = 20;
                  x = 0;
                  posX = 0;
                  rx = random(10, width-10);
                  ry = random(10, 0.25*height);
                }

                void draw(){
                  background(255);
                  if(keyPressed){
                    if(keyCode == LEFT && 0 <= posX){
                      x--;
                    } else if(keyCode == RIGHT && posX <= width-pWidth){
                      x++;
                    }

                  }
                  if(ry < width+10){
                    ry++;
                  } else {
                    life--;
                    rx = random(10, width-10);
                    ry = random(10, 0.25*height);
                  }

                  posX = 0.5*width-0.5*pWidth+x;
                  rect(posX , height-pHeight, pWidth, pHeight);
                  arc(rx, ry, 20, 20, 0, 2*PI);
                }
                 
                

Langsam nimmt das Ganze doch schon Form an!


Jetzt muss nur noch der Tropfen neu gezeichnet werden, wenn er unsere Platform trifft. Dazu muss der Tropfen auf der selben Höhe sein wie unsere Platform ry == width-pHeight. Außerdem muss der Tropfen sich zwischen den Koordinaten der Platform sein posX+pWidth >= rx >= posX.
In diesem Abschnitt werden auch die Punkte hinzugefügt.

                int life;
                int points;
                int pHeight;
                int pWidth;
                float posX;
                int x;
                float rx;
                float ry;

                void setup(){
                  background(255);
                  life = 10;
                  points = 0;
                  pHeight = 20;
                  pWidth  = 20;
                  x = 0;
                  posX = 0;
                  rx = int(random(10, width-10));
                  ry = int(random(10, 0.25*height));
                }

                void draw(){
                  background(255);
                  if(keyPressed){
                    if(keyCode == LEFT && 0 <= posX){
                      x--;
                    } else if(keyCode == RIGHT && posX <= width-pWidth){
                      x++;
                    }

                  }
                  if(ry < width+10){
                    ry++;
                  } else {
                    life--;
                    rx = int(random(10, width-10));
                    ry = int(random(10, 0.25*height));
                  }
                  if(ry == width-pHeight-10){
                    if(rx >= posX && rx <= posX+pWidth){
                      points++;
                      rx = random(10, width-10);
                      ry = random(10, 0.25*height);
                    }
                  }

                  posX = 0.5*width-0.5*pWidth+x;
                  rect(posX , height-pHeight, pWidth, pHeight);
                  arc(rx, ry, 20, 20, 0, 2*PI);
                }
                 
                

Was bleibt dazu noch zu sagen, eigentlich nicht viel. Die Erweiterung des Programms um eine Ausgabe des Lebens und der Punkte mittels text(x, y, "Hallo Welt") überlasse ich euch.
Hiermit sollte zumindest ein grundlegendes Verständnis für das Vorgehen vermittelt worden sein.

Zum Schluss findet ihr hier noch meine Version des Regenfängers. Allerdings objektorientiert! Wer sich traut, darf auch gerne mal einen Blick auf diesen Code werfen und die Vorteile zu dem hier vorliegenden Vorgehen rausfinden.