Sensitive Partikel in Processing
Powered by Processing.js
Untergliedert in zwei Teile besteht das Programm aus einem üblichen Processing-Sketch mit setup() / draw() Block, in welchem der Sketch initialisiert und gesteuert wird. Der zweite Teil beschreibt unser Model Circle (bzw. die Klasse Circle); die Beschaffenheit und Funktionalität die jeder Kreis im Sketchfenster mitbringt. Wir nehmen zuerst diesen – spannenderen Teil – genauer unter die Lupe.
Im dafür angelegten Tab Circle leiten wir die Klasse mit dem Ausdruck class ein und legen vorab zwischen den Geschweiften Klammern { … } die Eigenschaften des Kreises fest.
class Circle {
boolean iq;
float x, y;
float xTarget, yTarget;
float radius = 5;
//...
}
Neben den üblichen Eigenschaften wie Position und Radius besitzt jeder Kreis unteranderem Information über seine Cleverness iq (schlau oder dumm) und seine Zielposition xTarget, yTarget (alle hier definierte Variablen werden im Programm für jeden einzelnen Kreis vorhanden sein).Der weitere Code innerhalb des Circle Tabs beinhaltet den Konstruktor zur Initialisierung und einige Methoden (spezifische Fähigkeiten des Kreises). Die wichtigste von diesen ist mit dem Namen move() betitelt und ist für die Bewegung des Kreises verantwortlich. Die Logik hierbei ist eine Simple Frage: Wie nah ist die Maus, bzw. bewegt sich der Kreis zu ihr hin oder von ihr weg? Um ein Antwort zu erhalten, ermitteln wir zu Beginn die Distanz zwischen Maus und Kreis:
if (dist (x, y, mouseX, mouseY) <= RANGE) {
…
Ist diese Aussage wahr, befindet sich die Maus nah am Kreis und veranlasst diesen sich zu bewegen. Abhängig von seiner Cleverness (die Variable iq) wird das neue Ziel des Kreises entweder dichter zur Mausposition (dumm) oder weiter entfernt(schlau) definiert; und der Bewegungszustand slideStat auf wahr gesetzt. Mit diesem Schritt hat der Kreis ein Ziel und die Information sich zu bewegen.Im letzten Abschnitt der move() Methode wird der slideStat des Kreises erfragt und wenn true weiter zu seinem Ziel verschoben:
x -= (x - xTarget) / slideSpeed; y -= (y - yTarget) / slideSpeed;Die Methode bounceCircle() ist ein weiterer wichtiger Bestandteil der Kreis-Animation und stellt das Nichtverlassen des Sketchfensters sicher.
Im Sketch-Tab Circles-example-01 werden im setup() Block mit der Funktion createCircles() 40 Kreise erzeugt:
for (int i=0; i < circles.length; i++) {
float x = random (width);
float y = random (height);
circles[i] = new Circle (x, y);
}
Initialisiert und versehen mit einer zufälligen Position "existieren" diese Kreise nun in unserem Sketchfenster und müssen in jedem Frame aufgefordert werden auf die Maus zu reagieren. Dies passiert innerhalb des draw() Blocks. Wir laufen mit einer for-Schleife durch das circles Array und rufen die move() Methode auf, bevor wir letztendlich den Kreis an seiner jeweiligen Position mit dem ellipse() Befehl darstellen.Um die Funktionsweise abzubilden sind folgende Tastenbelegungen im Programm implementiert:
- n Sketch mit erneut mit 40 Kreisen befühlen
- t Kreis-Ziele anzeigen/ausblenden
- r Sensitiven Bereich anzeigen/ausblenden
Der Sketch
// Display setting: zeichne sensitiven Bereich
boolean dRange = true;
// Display setting: zeichne Ziel
boolean dTarget = true;
// Display setting: zeichne Schweif
boolean dTail = true;
// Array zum Ablegen aller circles
Circle circles[] = new Circle[40];
void setup() {
// Sketch-Einstellungen
size (550, 220);
smooth ();
// Erstelle circles an zufälligen Positionen
createCircles ();
}
void draw() {
background (79);
// Für jeden einzelnen circle im
// array 'circles'...
for (int i=0; i < circles.length; i++) {
// Bewege circle 'i'
circles[i].move ();
// Zeige debug-Informationen an,
// wenn diese aktiviert sind
if (dRange) displayRange(i);
if (dTarget) displayTarget(i);
if (dTail) displayTail(i);
// Lege die Füllfarbe für den 'circle' fest
// entsprechend seiner 'Fluchteigentschaft'
if (circles[i].iq) {
fill (229, 90, 38);
}
else {
fill (138, 170, 178);
}
// Setze Strichfarbe auf weiß
stroke (239);
// Zeichne den circle 'i' an seiner Position
ellipse (circles[i].x, circles[i].y, circles[i].radius*2, circles[i].radius*2);
}
}
// Methode zum Erstellen und Befüllen des 'circles'
// Arrays. Alle circles werden zufällig positioniert.
void createCircles () {
// Für jedes Feld im 'circles' array...
for (int i=0; i < circles.length; i++) {
// Ermittle Zufallsposition
float x = random (width);
float y = random (height);
// Erstelle neuen circle
circles[i] = new Circle (x, y);
}
}
// Methode zum Darstellen des sensitiven Bereichs
// um einen circle herum.
void displayRange (int id) {
// Wenn der circle 'id' sichtbar ist...
if (circles[id].radius > 0) {
// Aktiver 'range' Durchmesser
float diam = circles[id].RANGE * 2;
// Deaktiviere Füllfarbe
noFill ();
// Setze Linienfarbe
stroke (150);
// Zeichne Range-Bereich
ellipse (circles[id].x, circles[id].y, diam, diam);
}
}
// Methode zum Darstellen des Bewegungs-Ziels.
void displayTarget (int id) {
// Wenn circle 'id' sichtbar ist...
if (circles[id].radius > 0) {
// Setze Strichfarbe
stroke (239);
// Zeichne Linie zwischen aktueller circle-Position
// und dem circle-Ziel
line (circles[id].x, circles[id].y, circles[id].xTarget, circles[id].yTarget);
}
}
// Methode zum Darstellen des circle-Schweifs.
void displayTail (int id) {
// Wenn circle 'id' sichtbar ist...
if (circles[id].radius > 0) {
// Deaktiviere Füllfarbe
noFill ();
// Für alle Schweifpositionen
for (int i=circles[id].tailArr.length-1; i > 0; i--) {
// Passe Strichfarbe an
stroke (170, 170, 170, i*20);
// Passe Kreisradius an
float rad = circles[id].radius*8/i;
// Zeichne Schweifelement
ellipse (circles[id].tailArr[i][0], circles[id].tailArr[i][1], rad, rad);
}
}
}
// Tastatur-Event zum Einstellen der display settin
void keyPressed () {
if (key == 'b' || key == 'B') dTail = !dTail;
if (key == 'n' || key == 'N') createCircles();
if (key == 'r' || key == 'R') dRange = !dRange;
if (key == 't' || key == 'T') dTarget = !dTarget;
}class Circle {
// Größe des sensitiven Circle-Bereichs
float RANGE = 30;
// Bewegungszustand (ja/nein)
boolean slideStat = false;
// Cleverness (schlau=true,dumm=false)
boolean iq;
// Aktuelle Circle-Position
float x, y;
// Circle-Zielposition
float xTarget, yTarget;
// Circle Radius
float radius = 5;
// Fliehdistanz
float slideRange = 6;
float slideSpeed = 12;
// Maximale Anzahl an Schweifelementen
int maxTailNum = 21;
// Aktuelles Schweifelement
int tailCount;
// Schweif-array mit x/y Positionen
// für 'maxTeilNum' Schweifelemente
float[][] tailArr = new float[maxTailNum][2];
// Konstrutor Funktion zum Initialisieren
// eines neuen Circles.
Circle (float xPos, float yPos) {
// Übernimm Startposition
x = xPos;
y = yPos;
// Definieren zufälliges Start-Ziel
xTarget = random (x-2, x+2);
yTarget = random (y-2, y+2);
// Setze 'schlau/dumm' Status
// basierent auf 50/50 chance
iq = (random (1) < 0.5f);
tailCount = 0;
// Erstelle leeres Schweif-array und
// setze alle Positionen auf die aktuelle
for (int i=0; i < this.tailArr.length; i++) {
tailArr[i][0] = x;
tailArr[i][1] = y;
}
}
// Funktion zum Bewegen des Circles. Je nach 'cleverness'
// bewegt sich dieser zur Maus hin bzw. weg.
public void move () {
// Wenn die Distanz zwischen Circle und Maus-Position
// kleiner/gleich der sensitiven RANGE ist...
if (dist (x, y, mouseX, mouseY) <= RANGE) {
// Wenn Circle schlau ist...
if (iq) {
// Verschiebe Circle weg von der Maus
xTarget = x - (mouseX - x) * slideRange;
yTarget = y - (mouseY - y) * slideRange;
}else{
// Verschiebe Circle hin zur Maus
xTarget = mouseX + radius * 2;
yTarget = mouseY + radius * 2;
}
// Setze den Bewegungsstatus auf true
slideStat = true;
}
// Wenn Circle dumm ist...
if (!iq) {
// Wenn die Maus den Circle berührt...
if (dist (x, y, mouseX, mouseY) <= radius + 1) {
// Wenn Circle-radius größer als 0 ist...
if (radius > 0) {
// Reduziere Radius um 0.2
radius -= 0.2;
}else{
// Anderenfalls setze Radius auf 0
radius = 0;
}
}
}
// Wenn Bewegungsstatus 'true' ist...
if (slideStat) {
// Überprüfe ob Distanz zwischen Circle-Position
// und Circle-Ziel kleiner/gleich 1 ist...
if (dist (x, y, xTarget, yTarget) <= 1) {
// Wenn ja, setze Bewegungsstatus auf 'false'
slideStat = false;
}
// Teste ob sich der Circle aus dem
// Sketchfenster bewegt
bounceCircle ();
// Bewege den Circle einen 'slideSpeed'
// Schritt zu seinem Ziel
x -= (x - xTarget) / slideSpeed;
y -= (y - yTarget) / slideSpeed;
// Füge die Aktuelle Position zum Schweif hinzu
createTail ();
}else{
// Anderenfalls, bearbeite Schweif
removeTail ();
}
}
// Funktion zum Abprallen des Circles an den
// Rändern des Sketchfensters.
private void bounceCircle () {
// Linke Seite des Sketchfensters
if (x <= radius * 2) {
xTarget = x + (x - xTarget);
yTarget = y - (y - yTarget);
}
// Obere Seite des Sketchfensters
if (y <= radius * 2) {
xTarget = x - (x - xTarget);
yTarget = y + (y - yTarget);
}
// Rechte Seite des Sketchfensters
if (x >= width - radius * 2) {
xTarget = x - (xTarget - x);
yTarget = y - (y - yTarget);
}
// Untere Seite des Sketchfensters
if (y >= height - radius * 2) {
xTarget = x - (x - xTarget);
yTarget = y - (yTarget - y);
}
}
private void createTail () {
if (slideStat == true) {
if (tailCount > maxTailNum-1) {
tailCount = 0;
}
tailArr[tailCount][0] = x;
tailArr[tailCount][1] = y;
}
tailCount++;
}
private void removeTail () {
if (slideStat == false) {
for (int i=0; i < tailArr.length; i++) {
tailArr[i][0] = -radius * 1.2;
tailArr[i][1] = -radius * 1.2;
}
}
}
}