Processing Workshop | Teil 5 | Klassen und Objekte, Bewegung

Markus Walthert

Objekt
Die Stärke von Objekten beim programmieren ist, dass ein einmal definiertes und beschriebenes Objekt unendlich oft als Instanz weiterverwendet werden kann. Indem man dem Objekt unterschiedliche Parameter mitgibt, kann es auch jedes Mal seine Eigenschaften wie z.B. das Aussehen oder Bewegungsverhalten ändern.

In diesem Beispiele will ich Kreise auf der Screen verteilen. In der setup() Methode wird jedem Kreis ein zufällig generierter Wert für seine Startposition (x/y), seine Geschwindigkeit (vx/vy) und seine Größe (radius) erzeugt.

Der Kreis selber ist in einer Klasse mit dem Namen Ball beschrieben. Eine Klasse beginnt mit der Definition class Name{ und der Methode zum Aufruf der Klasse, hier Ball(){. Ob die Klasse im selben Script wie die Hauptklasse steht oder eine eigene Datei, spielt keine Rolle. Bei der Namensgebung ist lediglich wichtig, dass die Datei gleich heißt wie die Klasse auch. In der Regel beginnen Klassen mit einem Großbuchstaben.

Im Processingeditor können zusätzlich Dateien als Tabs abgelgt werden. Erstellt weden die Tabs mit dem Pfeil rechts oben.
Editor-340x249 in Processing Workshop | Teil 5 | Klassen und Objekte, Bewegung
In der sogenannten Superklasse, im Beispielbild oben die Datei _01, kann Ball nun wie ein Datentyp behandelt werden. Da ich die Möglichkeit für mehrere Bälle haben will, definiere ich Ball als Array Ball[] und gebe den Instanznamen myBalls. Damit der Array myBalls überhaupt zum Leben gebracht wird, muss ich ihn, ähnlich wie die Kugeln später auch, über das Schlüsselwort new erzeugen. Damit der Array auch weiß, wie viel Speicher für ihn reserviert werden soll, wird mit der Variable N für die Anzahl der Kugeln auch festgelegt, wie viele Kreise myBalls[] beinhalten kann.

In einer for-Schleife werden danach die Kreise ebenfalls mit new erzeugt
myBalls[i] = new Ball(x, y, vx, vy, radius);
und jeweils aus dem Code der Klasse Ball eine Instanz erzeugt. In der Klasse selber werden in diesem Beispiel lediglich die übertragenen Parameter x, y, vx, vy, radius in global definierten Variablen, für jeden Ball einzeln, abgelegt. Global definierte Variablen können überall in der Klasse verwendet werden, die lokale Variable _x hingegen könnte nur in der Methode Ball{} verwendet werden.
Die Zählervariable i dient gleichzeitig zum durchnummerieren der einzelnen Kreise. Beim ausführen des Codes, sind die Kreise nun alle im Speicher vorhanden, aber noch nicht sichtbar.

Make the things move
Den kontinuierlichen Aufruf der Methode draw() kann ich nun auch gleich benutzen, um den Kreis zu bewegen. Damit alle Kreise angezeigt werden, müssen in jedem Renderzyklus immer alle Kreise über eine for-Schleife abgearbeitet werden. Bei einer Framerate von 25 Zyklen pro Sekunden und 100 Kreisen müssen also je fünfunzwanzigstel Sekunde 100 Kreise gerendert werden.

Bewegt werden die Kreise in der Ball Klasse. In der Superklasse wird die Methode run() über die Punktsyntax aufgerufen
myBalls[i].run();
und jeweils mit der jeder Kugel eigenen Geschwindigkeit vx und vy bewegt, indem bei jedem Aufruf der Wert vx rsp. vy zur aktuellen Position hinzuaddiert wird.
x = x + vx;

Erreicht der Kreis den Screenrand, muss die Richtung umgedreht werden. Dazu ändert man einfach das Vorzeichen.
vx = -vx;

Um das folgende Beispiel anzuzeigen, bitte auf das Vorschaubild klicken.

Ball[] myBalls;
int N = 1; // Anzahl der Bälle
void setup(){
  size(320, 240);
  frameRate(25);
  myBalls = new Ball[N];
  for (int i = 0; i < N; i++)  {
    float x = random(0, width);
    float y = random(0, height);
    float vx = random(-7, 7);
    float vy = random(-7, 7);
    float radius = random(3, 9);
    myBalls[i] = new Ball(x, y, vx, vy, radius);
  }
}
void draw(){
  background(0);
  for (int i = 0; i < N; i++)  {
    myBalls[i].run();
  }
}
class Ball{
  float x;
  float y;
  float vx;
  float vy;
  float radius;
  Ball(float _x, float _y, float _vx, float _vy, float _radius){
    x = _x;
    y = _y;
    vx = _vx;
    vy = _vy;
    radius = _radius;
  }
  void run()  {
    drawShape();
 
    x = x + vx;
    y = y + vy;
 
    /*
    * Abfragen nach Kollisionen links, rechts, oben und unten zur Screen
    */
    if (x < radius)    {
      x = radius;
      vx = -vx;
    }
    if (x > width - radius)    {
      x = width - radius;
      vx = -vx;
    }
    if (y < radius)    {
      y = radius;
      vy = -vy;
    }
    if (y > height - radius)    {
      y = height - radius;
      vy = -vy;
    }
 
  }
  void drawShape()  {
    ellipseMode(CENTER_RADIUS);
    noStroke();
    fill(255);
    ellipse(x, y, radius, radius);
  }
}