AnimationProgrammierte Bewegung

Kern dieser Lesson ist die Vermittlung der Grundprinzipien der Animation mit Processing. Ausgehend vom einfachsten Prinzip der Bildfolge, welches oft in der Spielprogrammierung angewendet wird, folgen Erklärungen zu linearer und non-linearer Animation. Dabei stehen eindimensionale Bewegungen ebenso im Mittelpunkt wie die Bewegung auf Kreisen, Ellipsen und Kreissegmenten mit Hilfe von trigonometrischen Funktionen (Sinus und Kosinus),

Bildanimation

Dieser erste Abschnitt steht für die digitale Form eines Daumenkinos. Durch die schnelle Abfolge von ähnlichen, aber nicht gleichen, Bildern in ausreichend zügiger Geschwindigkeit kommt es zur Wahrnehmung einer Bewegung. Alle Bilder werden dafür im Teil des setup() geladen und im draw() je nach Bedarf abgebildet. Die Entscheidung welches der Einzelbilder auf der Zeichenfläche erscheint wird beim automatisierten Ablauf anhand einer Variable gefällt. Global instanziert bildet sie das Gedächtnis und gibt Aufschluss über die Abspielposition. Mögliche weitere Einflüsse können aber auch Eingaben durch Tastatur und Maus sein.
Stop Trick Animation in Processing

Vier unterschiedliche Motive einer Szene in der Bart Skateboard fährt werden mit einer Geschwindigkeit von sechs Bildern pro Sekunde angezeigt. Jedes Bild wird im setup() in eine der vier Bildvariablen geladen, eine weitere globale Variable namens "frame" speichert welches Bild angezeigt werden soll. Am Ende eines jeden draw() -Durchlaufs wird der Wert von "frame" um eins erhöht. Wenn dieser größer als fünf ist, kommt es zu einem Zurücksetzen auf eins - die Animation beginnt erneut.

// globale Variable zum Ablegen
// der Einzelbilder für die Anim.
PImage img1;
PImage img2;
PImage img3;
PImage img4;

// globale Variable zum Ablegen 
// der Abspielposition
int frame;

void setup () {
  // Größe des Sketches
  size (113, 107);
  // Setze dir Bildanzahl 
  // pro Sekunde auf 6
  frameRate (6);
  
  // Beginne mit Bild 1
  frame = 1;
  
  // Lade alle vier bilder
  img1 = loadImage ("bart01.png");
  img2 = loadImage ("bart02.png");
  img3 = loadImage ("bart03.png");
  img4 = loadImage ("bart04.png");
}

void draw () {
  // Setzt die Abspielposition auf das 
  // erste Bild wenn Programm das fünfte 
  // spielen würde (größer 4) 
  if (frame > 4) {
    frame = 1;
  }
  
  // Spielt durch bedingte Abfragen immer 
  // das Einzelbild für die aktuelle 
  // Position in der Animation ('frame')
  if (frame == 1) {
    image (img1, 0, 0);
  }
  if (frame == 2) {
    image (img2, 0, 0);
  }
  if (frame == 3) {
    image (img3, 0, 0);
  }
  if (frame == 4) {
    image (img4, 0, 0);
  }
  
  // erhöht die Abspielposition um eins
  frame = frame + 1;
}

Lineare Animation

Da uns die Mittel gegeben sind durch diverse Zeichenfunktionen den Bildaufbau zu bestimmen, können wir grafische Element durch Modifikation der Position bewegen. Im vorherigen Kapitel, zum Thema: Globale Variablen, haben wir bereits eine Linie vom oberen Bildschirmrand nach unten fahren lassen. Diesen Fall der Animation nehmen wir nun genauer unter die Lupe.
Die Linie bewegte sich mit einer konstanten Geschwindigkeit auf der y-Achse. Zwischen allen Bildern wurde die selbe Distanz zurückgelegt - die Beschleunigung können wir deshalb mit 0 angeben. Dieser Fakt macht die Animation zu einer linearen Animation: »Konstante Zustandsänderung zwischen allen Einzelbildern.« Erhöhen wir den Weg der zwischen den Einzelbildern beschritten wird, erhalten wir eine schnellere Animation, sie bleibt aber linear.
Alle drei folgenden Beispielen wiederholen die angesprochene Animation aus Kapitel Variablen II. Beispiel zwei und drei bedienen sich weiterhin der if-Bedingung um Kollisionen mit den Außenseiten des Sketchfensters zu simulieren.
Bsp.: lineare Bewegung, eine Richtung
Gezeichnet wird die gleichförmige Ellipse am Wert der globalen Variable xpos und der Hälfte der Höhe. In jedem Durchlauf des draw()-Blocks erhöhen wir den Wert von xpos um 1, erhalten damit eine Bewegung des Kreises auf der x-Achse vom linken in den rechten Fensterabschnitt. Diese Vorgang findet bis zum Schließen des Fensters statt. Die Position des Kreises würde demnach innerhalb weniger Momente außerhalb des für uns sichtbaren Bereiches sein. Um dies zu lösen integrieren wir eine Bedingung nach dem Aktualisieren der Position (xpos). Wir fragen nach dem aktuellen Wert von xpos und schreiten ein, wenn sich der Kreis nicht mehr im Sketchfenster befindet. Die Intervention schlägt sich im Zurücksetzen des Wertes von xpos auf 0 nieder - die Animation beginnt wieder von vorn. Um den Kreis auf beiden Seiten (links und rechts) komplett verschwinden zu lassen, testen wir auf xpos < width + durchmesser und setzen der Wert im if-Block auf -durchmesser.
// gloable Variable für 
// die Ablage der Position
float xpos = 0;
// und den Kreisdurchmesser
float durchmesser = 30;

void setup () {
  // Größe des Sketches definieren
  size(320, 240);
  // aktiviere Kantenglättung
  smooth ();
}

void draw () {
  // Hintergrund leeren
  background (45);
  
  // Position modifiziere
  xpos = xpos + 1;
  
  // Zurücksetzen der Kreisposition auf die linke 
  // Bildschirmseite wenn die Position größer als 
  // Bildschrimbreite + Durchmesser ist
  if (xpos > width + durchmesser) {
    xpos = -durchmesser;
  }
  
  // Zeichnen des Kreises an die aktuelle Position
  ellipse (xpos, height / 2, durchmesser, durchmesser);
}
Bsp.: lineare Bewegung, loop
Als zweiten Schritt geben wir uns die Auflage den Kreis auf der Horizontalen pendeln zu lassen. Zur Realisierung bedarf es der Information in welcher Richtung wir ihn momentan bewegen sollen. Dafür legen wir die Variable geschwindigkeit an, die ebenfalls den Weg beinhaltet, um welchen der Kreis zwischen den Einzelbilden verschoben wird. Im vorherigen Beispiel haben wir dies zu Beginn des draw() mit xpos + 1 definiert. Neben dieser änderung kommt eine zweite if-Bedingung hinzu. Wir müsse bei Beiden, der linken und rechten Fensterseite, intervenieren, wenn sich der Kreis aus unserer Fläche bewegen sollte. Diese Eingriffe bewirken eine Umkehrung der Richtung, festgelegt durch geschwindigkeit. Wenn der Wert von geschwindigkeit positiv ist, bewegt sich der der Kreis von links nach rechts. Ist er negativ, von rechts nach links. Das Invertieren geschieht durch die Multiplikation mit -1.
// gloable Variable für 
// die Ablage der Position
float xpos;
// Positionsänderung pro Einzelbild
float geschwindigkeit = 2;
// Kreisdurchmesser
float durchmesser = 30;

void setup () {
  // Größe des Sketches definieren
  size(320, 240);
  // aktiviere Kantenglättung
  smooth ();
  // Startposition
  xpos = width / 2;
}

void draw () {
  // Hintergrund leeren
  background (45);

  // Umkehr der Richtung wenn die Position 
  // des Kreises sich am rechten Fensterrand ist
  if (xpos > width - durchmesser / 2) {
    geschwindigkeit = geschwindigkeit * -1;
  }
  // Umkehr der Richtung wenn die Position 
  // des Kreises sich am linken Fensterrand ist
  if (xpos <  durchmesser / 2) {
    geschwindigkeit = geschwindigkeit * -1;
  }

  // Position modifizieren, 'geschwindigkeit' 
  // kann positiv oder negativ sein (siehe: * -1)
  xpos = xpos + geschwindigkeit;

  // Zeichnen des Kreises an die aktuelle Position
  ellipse (xpos, height / 2, durchmesser, durchmesser);
}
Momentan wird die änderung der Richtung mit zwei if-Bedingungen kontrolliert. Innerhalb dieser Bedingungen tauschen wir das Vorzeichen unsers Wertes. Eine kompaktere Version ist die Kombination beider Bedingungen innerhalb einer if-Abfrage mit dem logischen || (ODER) Ausdruck. Im Block multiplizieren wir geschwindigkeit mit -1 und kehren dabei das Vorzeichen um - ohne den absoluten Wert zu modifizieren.
if (xpos > width - durchmesser || xpos < durchmesser / 2) {
  geschwindigkeit = geschwindigkeit * -1;
}
Bsp.: lineare Bewegung, frei
In Beispiel drei werden alle Elemente, welche die zur Animation der Kugel auf der x-Achse geführt haben, ebenfalls auf die y-Achse angewendet. D.h. wir benötigen eine weitere Variable yPos zur Ablage der Position und eine Variable yGeschwindigkeit um die Positionsänderung zu beschreiben. Weiterhin testen wir auf die Ober- und Unterseite des Sketchfensters.
Das Resultat ist ein sich frei bewegender Kreis der von allen Seiten reflektiert wird.
// gloable Variable für 
// die Ablage der Position
float xpos;
float ypos;
// Positionsänderung pro Einzelbild
float xGeschwindigkeit = 2;
float yGeschwindigkeit = 2;
// Kreisdurchmesser
float durchmesser = 30;

void setup () {
  // Größe des Sketches definieren
  size(320, 240);
  // aktiviere Kantenglättung
  smooth ();
  // Startposition
  xpos = width / 2;
  ypos = height / 2;
}

void draw () {
  // Hintergrund leeren
  background (45);

  // rechter Fensterrand
  if (xpos > width - durchmesser / 2) {
    xGeschwindigkeit = xGeschwindigkeit * -1;
  }
  // linker Fensterrand
  if (xpos < durchmesser / 2) {
    xGeschwindigkeit = xGeschwindigkeit * -1;
  }
  // unterer Fensterrand
  if (ypos > height - durchmesser / 2) {
    yGeschwindigkeit = yGeschwindigkeit * -1;
  }
  // oberer Fensterrand
  if (ypos < durchmesser / 2) {
    yGeschwindigkeit = yGeschwindigkeit * -1;
  }
  
  // Position modifizieren, 'geschwindigkeit' 
  // kann positiv oder negativ sein (siehe: * -1)
  xpos = xpos + xGeschwindigkeit;
  ypos = ypos + yGeschwindigkeit;

  // Zeichnen des Kreises an die aktuelle Position
  ellipse (xpos, ypos, durchmesser, durchmesser);
}

Non-lineare Animation

Im alltäglichen Leben treffen wir selten auf Bewegungsabläufe linearer Art. Im Normalfall haben wir es permanent mit beschleunigten bzw. gebremsten Abläufen zu tun. In diesem Gebiet findet eine positive oder negative Steigerung der Zustandänderungen statt - man bezeichnet dies auch als Beschleunigung.
Jegliches mobiles Gefährt steigert seine Geschwindigkeit aus der Ruhephase und fällt wiederum nicht sofort wieder in den Stillstand zurück. In dem folgenden Abschnitt beschäftigen wir uns mit der Simulierung dieser Prozesse und versuchen Lösungen für diese Art von profanen Vorgängen zu finden.
Bsp.: non-lineare Animation, zufällig
Um die Schrittweite innerhalb der Animation zu variieren bedienen wir uns im folgenden Beispiel des bekannten random()-Befehls. Das Resultat summiert auf die aktuelle Position verschiebt den Kreis von Bild zu Bild um unterschiedliche Distanzen.
Ausgeführt erhalten wir eine stockende Animation. Dies liegt nicht an der Geschwindigkeit unseres Computers, sondern an dem durch random() generierten Chaos in der Berechnung von xpos. Es ist kann dazu kommen, dass die Position in Bild 14 sieben Pixel erhöht wurde, und in Bild 15 um zwei vermindert. Unsere Wahrnehmug erkennt darin keine wiederkehrende Merkmale (homogene Tendenz) - außer das sich der Kreis scheinbar nach rechts bewegt. (Kommentare wurden zu übersichtlichkeit entfernt, siehe: lineare Bewegung - eine Richtung).
float xpos = 0;
float durchmesser = 30;

void setup () {
  size(320, 240);
  smooth ();
}

void draw () {
  background (45);
  
  // addiere zufällige Schrittweite
  // auf die aktuelle Position, 
  // vorwiegend in positiv
  xpos = xpos + random (-3, 7);
  
  // Zurücksetzen der Kreisposition
  if (xpos > width + durchmesser) {
    xpos = -durchmesser;
  }
  
  // Zeichnen des Kreises an die aktuelle Position
  ellipse (xpos, height / 2, durchmesser, durchmesser);
}
Bsp.: non-lineare Animation, gleichförmig
Für den nächsten Schritt bedarf es einer weiteren Variable. In ihr legen wir die Zielposition unseres Kreises ab und berechnen in jedem draw()-Durchlauf die momentane Distanz zwischen beiden Punkten. Im Schritt der Positionsaktualisierung addieren wir keinen fixen oder zufälligen Wert, sondern immer ein 60tel des verbleibenden Weges. Demnach beginnt die Animation mit einer großen Schrittweite und schwacht mit dem Näherkommen ab. Da wir jedoch nie unsere Zielposition erreichen werden, muß das Ziel weiter entfernt sein als in der if-Bedingung zum Neustarten abgefragt.
float xpos;
float ziel;
float durchmesser = 30;

void setup () {
  size(320, 240);
  smooth ();
  
  xpos = 0;
  // Zielposition muss größer sein 
  // als der in der 'if'-Abfrage 
  // getestete Wert, sonst Programmstopp 
  ziel = width + durchmesser * 2;
}

void draw () {
  background (45);
  
  // momentaner Abstand zwischen 
  // aktueller- und Zielposition
  float distanz = ziel - xpos;
  
  // addiere ein 60'tel der Distanz 
  // auf die aktuelle Position
  xpos = xpos + distanz / 60;
  
  // Zurücksetzen der Kreisposition
  if (xpos > width + durchmesser) {
    xpos = -durchmesser;
  }
  
  // Zeichnen des Kreises an die aktuelle Position
  ellipse (xpos, height / 2, durchmesser, durchmesser);
}
Bsp.: non-lineare, gleichförmige Animation + Mausklick
In diesem Beispiel entfallen alle Bedingungen zur Positionskontrolle des Kreises. Er kann sich nicht aus dem Sketchfensterbewegen - die Position wird immer mit dem Klicken durch die Maus definiert. Der Kreis folgt also unseren Anweisungen.
// aktuelle Position
float xpos;
float ypos;
// aktuelles Ziel
float xZiel;
float yZiel;
float durchmesser = 30;

void setup () {
  size(320, 240);
  smooth ();
  
  // Startposition
  xpos = 0;
  ypos = 0;
  // erstes Ziel
  xZiel = width / 2;
  yZiel = height / 2;
}

void draw () {
  background (45);
  
  // Abstände zwischen 
  // aktueller- und Zielposition
  float xd = xZiel - xpos;
  float yd = yZiel - ypos;
  
  // addiere ein 20'tel der Distanz 
  // auf die aktuelle Position
  xpos = xpos + xd / 20;
  ypos = ypos + yd / 20;
  
  // Zeichnen des Kreises an die aktuelle Position
  ellipse (xpos, ypos, durchmesser, durchmesser);
}

// Wenn die Maus gedrückt wurde
void mousePressed () {
  // setze neue Zielposition
  xZiel = mouseX;
  yZiel = mouseY;
}
Bsp.: non-lineare Animation, trigonometrische Basis
Es existieren bereits einige mathematische Grundlagen um gleichförmige Animationsabläufe zu simulieren. Zwei pragmatische sind die aus dem Matheunterricht bekannten Sinus & Kosinus, zur Winkel- und Kreisberechnung.
  • cos() Kosinuswert für einen gegeben Winkel cos()
  • sin() Sinuswert für einen gegebenen Winkel sin()
Im Bereich von zwei PI (Kreiszahl) bewegt sich die Kurve einmal durch den kompletten Bereich von 0 zu 1 und wieder zurück. Diesen Wert sehen wir als relative Position auf unserer x-Achse - multiplizieren ihn demnach mit der Breite unserer Sketches. Der Kurvenverlauf der Sinuskurve auf der y-Achse spiegelt folglich die Position des Kreises wieder.
Führen wir die folgenden Zeilen aus, ähnelt das Resultat der Draufsicht eines Pendels. An der linken und rechten Fensterseite kommt es zu einem Abbremsen - in der Mitte besitzt der Kreis seine maximale Geschwindigkeit. Die Variable pos wird zur Steuerung der Bewegung genutzt. Sie beinhaltet einen Wert zwische 0 und zwei PI, ein vollständiger Kurvendurchlauf. Momentan addieren wir in jedem draw()-Durchlauf 0.1 auf pos. Je größer der Summand, so schneller der Ablauf der Animation.
// dient zur Ablages des Animationsfortgangs 
// (beinhaltet aber nicht die exakte Position)
float pos = 0;

void setup () {
  size(320, 240);
  smooth ();
}

void draw () {
  background (45);
  
  // setze 'pos' auf 0 zurück wenn die 
  // Sinuskurve einmal durchlaufen ist
  if (pos > PI * 2) {
    pos = 0;
  }
  
  // erhöhe 'pos' pro Bild um 0.1 'pos' 
  // bewegt sich immer zwischen 0 und 2*PI
  pos = pos + 0.1;
 
  // Pendelzentrum, zwischen positivem und 
  // negativem Kurvenabschnitt
  float xZentrum    = width / 2;
  // momentaner Ausschlag des Pendels
  float xAmplitude  = width / 2 * sin (pos);
  // Summe ist die momentane Position
  float xpos        = xZentrum + xAmplitude;
  
  ellipse (xpos, height / 2, 30, 30);
}

Kreisbewegung

Diese Art der Bewegung findet auf einem imaginären Kreis statt. Auf der Grundlage der Position, des Radius und der Winkelangabe kann die Koordinate auf der Kreisbahn bestimmt werden. Processing bietet die trigonometrischen Funktionen sin() & cos() an, welche für gegebenen Winkel und Radius den Abstand zum Zentrum des Kreises berechnen lassen.
  • cos() gibt den Kosinuswert zwischen -1 und 1 für ein gegebenes Bogenmaß zurück. cos()
    println (cos (PI / 3));  // 0.49999997
    println (cos (PI));  // -1.0
  • sin() gibt den Sinuswert zwischen -1 und 1 für ein gegebenes Bogenmaß zurück. sin()
    println (sin (PI / 3));  // 0.86602545
Gleichförmige Kreisbewegung in Processing

Im folgenden Beispiel hält die globale Variable angle die Position der Ellipse auf der Kreisbahn. Der Wert 0.004 entspricht der Rotationsgeschwindigkeit um das Zentrum xcenter und ycenter und wird jedes Einzelbild auf angle aufgeschlagen, es kommt zur Kreisbewegung. Die genauen Koordinaten werden in jedem Bild berechnet und in x und y abgelegt.

float xcenter;    // Mittelpunkt auf der x-Achse
float ycenter;    // Mittelpunkt auf der y-Achse
float rad = 75;   // Radius der Kreisbahn
float angle;      // aktueller Rotationswinkel
 
void setup () {
  size(320, 240);
  smooth ();
  noStroke ();
  background (255);
  
  // Rotationsmittelpunkt
  xcenter = width / 2;
  ycenter = height / 2;
}

void draw () {
  fill (255, 10);
  rect (0, 0, width, height);
 
  // Verschieben des Rotationswinkels
  angle += 0.04; 
  // Berechnung der aktuellen Position
  float x = xcenter + cos (angle) * rad;
  float y = ycenter + sin (angle) * rad;
  // Zeichnen des Kreises
  fill (0);
  ellipse (x, y, 30, 30);
}
Bewegung auf Kreisabschnitt in Processing

Um nur einen bestimmten Abschnitt der Kreisbahn abzufahren muss der Wertebereich des Winkels eingeschränkt werden. Der Startwert für angle ist in diesem Sketch mit dem Wert von angleStart angegeben. Wie im vorherigen Beispiel wird der Winkel von Bild zu Bild um einen festen Betrag erhöht. Direkt nach dem erhöhen des Winkels überprüft eine if-Abfrage ob das Limit überschritten wurde. Ist dies der Fall, wird der Winkel wieder auf den Startwert angleStart zurückgesetzt. Der Wertebereich setzt sich demnach wie folgt zusammen:

  • Startwinkel der Betrag der Variable angleStart
  • Endwinkel der abgefrage Winkel in der if-Bedingung

float xcenter;    // Mittelpunkt auf der x-Achse
float ycenter;    // Mittelpunkt auf der y-Achse
float rad = 75;   // Radius der Kreisbahn
float angle = 0;  // aktueller Rotationswinkel
float angleStart = 0.4; // Startwinkel

void setup () {
  size(320, 240);
  smooth ();
  noStroke ();
  background (255);
 
  // Rotationsmittelpunkt
  xcenter = width / 2;
  ycenter = height / 2;
  angle = angleStart;
}
 
void draw () {
  fill (255, 10);
  rect (0, 0, width, height);
 
  // Verschieben des Rotationswinkels
  angle += 0.04;
  
  // Zurücksetzen des Winkels wenn End-
  // winkel von einem PI erreicht ist
  if (angle > PI + angleStart) {
    angle = angleStart;
  }
  
  // Berechnung der aktuellen Position
  float x = xcenter + cos (angle) * rad;
  float y = ycenter + sin (angle) * rad;
  
  // Zeichnen des Kreises
  fill (0);
  ellipse (x, y, 30, 30);
}
Bewegung auf einer Ellipse in Processing

Der Unterschied zwischen einem gleichförmigen Kreis und einer Ellipse besteht in der Varianz des Radius. Die Punkte auf einem Kreis besitzen immer den selben Abstand zum Zentrum des Kreises - bei einer Ellipse verändert sich dieser Abstand (Radius) von Punkt zu Punkt.
In diesem Beispiel wurden deshalb zusätzlich die beiden Variablen xRad und yRad eingeführt. Für jedes Bild wird jeweils der Radius der Ellipse für die x und y-Achse berechnet. Dabei kann durch das Verschieben der Maus deren Betrag geändert werden.

float xcenter;    // Mittelpunkt auf der x-Achse
float ycenter;    // Mittelpunkt auf der y-Achse
float rad = 75;   // Radius der Kreisbahn
float angle;      // aktueller Rotationswinkel
 
void setup () {
  size(320, 240);
  smooth ();
  noStroke ();
  background (255);
  
  // Rotationsmittelpunkt
  xcenter = width / 2;
  ycenter = height / 2;
}

void draw () {
  fill (255, 40);
  rect (0, 0, width, height);
 
  // Verschieben des Rotationswinkels
  angle += 0.04;
  
  // Berechnen des Radiuses für x und y Achse
  float xRad = ((float) mouseX / width) * rad;
  float yRad = ((float) mouseY / height) * rad;
  
  // Berechnen der aktuellen Position
  float x = xcenter + cos (angle) * xRad;
  float y = ycenter + sin (angle) * yRad;
  
  // Zeichnen des Kreises
  fill (0);
  ellipse (x, y, 30, 30);
}
Bewegung auf einer Spirale in Processing

Ähnlich wie bei der Ellipse finden wir bei der Spirale einen 'dynamischen' Faktor welche die Form unserer Kreisbewegung beeinflusst. Für jeden Schritt um welchen der Winkel verschoben wird, erhöhen wir ebenfalls den Radius rad um den Wert 0.2. Dieser Wert beschreibt wie dicht oder offen unsere Spirale wächst. Verglichen mit dem Beispiel der gleichförmigen Kreisbewegung ist dies der einzige Unterschied, auch wenn das Resultat komplexer erscheint.

float xcenter;   // Mittelpunkt auf der x-Achse
float ycenter;   // Mittelpunkt auf der y-Achse
float rad = 0;   // Radius der Kreisbahn
float angle = 0; // aktueller Rotationswinkel
 
void setup () {
  size(320, 240);
  smooth ();
  noStroke ();
  background (255);
 
  // Rotationsmittelpunkt
  xcenter = width / 2;
  ycenter = height / 2;
}
 
void draw () {
  fill (255, 10);
  rect (0, 0, width, height);
 
  // Verschieben des Rotationswinkels
  angle += 0.04;
  // Vergrößern des Radius pro Bild
  rad += 0.2;
  
  // Berechnung der aktuellen Position
  float x = xcenter + cos (angle) * rad;
  float y = ycenter + sin (angle) * rad;
  
  // Zeichnen des Kreises
  fill (0);
  ellipse (x, y, 10, 10);
}
Oszillierende Bewegung in Processing

Um realistisch bzw. organisch anmutende Bewegungen zu formulieren lohnt sich ein Blick auf sin() und cos(). Als trigonometrische Funktionen ermöglichen sie im Zusammenhang mit der Kreiszahl PI sauber erscheinende Schwingungen. Der Bewegungsverlauf verhält sich dabei wie der Graf einer Sinus-, bzw. Kosinusfunktion.

float radBase = 50;     // Basisradius
float radDiff = 20;     // Differenzradius
float oscillation = 0;  // aktueller Zustand

void setup () {
  fill (0);
  smooth ();
}

void draw () {
  background (255);
  
  /* Zähler der oszillierenden Schwingung 
   * im Bereich zwischen 0 und zwei PI.
   */
  oscillation += 0.1;
  if (oscillation > TWO_PI) {
    oscillation = 0;
  }
  
  // Berechne den Kreisradius
  float rad = radBase + radDiff * sin (oscillation);
  // Zeichne den Kreis
  ellipse (width / 2, height / 2, rad, rad);
}