I decided to use the creature I designed in previous assignments, to develop an ecosystem of two different species living together inside of a water tank. There is a particle system of small creatures (boids) flocking together, and a larger creature (predator) that follows and repels them as they “swim”.
The object “predator” is an extension of the object “boid”, so that they all have the same swimming behaviors but with different values according to their size.
Processing code:
ArrayList boids = new ArrayList(); // array of boids
Predator predator; // predator class
PVector mouse; // mouse vector
int boidNum = 100; // initial number of boids
float repelRad = 60; // size of repeller force
float timer; // timer for oscillation
void setup() {
frameRate(30);
size(800, 800, P2D);
// create boids
for (int i=0; i boids) {
avoidForce(boids);
approachForce(boids);
alignForce(boids);
}
void update() {
// Calculate and add next position (by K)
PVector nextPos = loc;
nextPos.add(vel);
// Calculate distance from the center of screen
PVector center = new PVector(width/2, height/2);
float dist = nextPos.dist(center);
// Check edges and bounce according to distance
if (dist >= height/2) {
vel.x = vel.x * mass;
vel.y = vel.y * mass;
}
vel.add(acc);
loc.add(vel);
acc.mult(0); // reset acc every time update is called
vel.limit(4); // limit on speed
}
void applyF(PVector force) {
//F=ma
force.div(mass);
acc.add(force);
}
void display() {
update();
noStroke();
fill(255, 200);
ellipse(loc.x, loc.y, mass, mass);
}
// AVOID
void avoidForce(ArrayList boids) {
float count = 0; // keep track of how many boids are close
PVector locSum = new PVector(); // store positions
for (Boid other : boids) {
int separation = mass + 25; // separation, using mass as reference
PVector dist = PVector.sub(other.getLoc(), loc); // distance to other boid
float d = dist.mag();
if (d != 0 && d0) {
locSum.div(count); // divide by number of other positions
PVector avoidVec = PVector.sub(loc, locSum);
avoidVec.limit(maxForce*3);
// Avoid border of circle (math by K)
PVector center = new PVector(width/2, height/2);
float dist = loc.dist(center);
float reduce = dist / (width/2); // 0..1
PVector avoidBorder = new PVector(center.x, center.y);
avoidBorder.sub(loc);
avoidBorder.normalize();
avoidBorder.mult(reduce * 5);
avoidVec.add(avoidBorder);
applyF(avoidVec);
}
}
// APPROACH
void approachForce(ArrayList boids) {
float count = 0; // keep track of how many boids are close
PVector locSum = new PVector(); // store locations
for (Boid other : boids) {
int approachRadius = mass + 40; // radius of distance between boids
PVector dist = PVector.sub(other.getLoc(), loc);
float d = dist.mag();
if (d != 0 && d0) {
locSum.div(count); // divide by number of other positions
PVector approachVec = PVector.sub(locSum, loc);
approachVec.limit(maxForce);
applyF(approachVec);
}
}
// ALIGN
void alignForce(ArrayList boids) {
float count = 0; // keep track of how many boids are close
PVector velSum = new PVector(); // store velocities
for (Boid other : boids) {
int alignRadius = mass + 100; // radius of distance between boids
PVector dist = PVector.sub(other.getLoc(), loc);
float d = dist.mag();
if (d != 0 && d0) {
velSum.div(count); // divide by number of other positions
PVector alignVec = velSum;
alignVec.limit(maxForce);
applyF(alignVec);
}
}
// REPEL (NEW!!)
void repelForce(PVector repeller, float radius) {
PVector futPos = PVector.add(loc, vel); // calculate future position
PVector dist = PVector.sub(repeller, futPos);
float d = dist.mag();
if (d <= radius) {
PVector repelVec = PVector.sub(loc, repeller);
repelVec.normalize();
if (d != 0) {
float scale = 1.0/d; // stronger if boid is closer to the repeller
repelVec.normalize();
repelVec.mult(maxForce*7);
if (repelVec.mag()<0) { // avoid the repeller
repelVec.y = 0;
}
}
applyF(repelVec);
}
}
// Get acces loc and vel for any boid.
PVector getLoc() {
return loc;
}
PVector getVel() {
return vel;
}
}
class Predator extends Boid { // Predator is a child of Boid
int maxForce = 10; // higher effect of the force
// Constructor
Predator(PVector _loc, int scope) {
super(_loc);
mass = 30;
}
void display() {
update();
fill(255, 150);
noStroke();
float theta = vel.heading2D(); // angle for oscillation
// Body
ellipse(loc.x, loc.y, mass, mass);
ellipse(loc.x, loc.y, mass*.8, mass*.8);
// Legs
pushMatrix();
translate(loc.x, loc.y);
rotate(theta);
stroke(255, 200);
strokeWeight(4);
displayLeg(mass * 0.75, 0.75, 0.3);
displayLeg(mass * 0.75, 0.25, 0.1);
displayLeg(mass * 0.75, 0.25, 0.1);
displayLeg(mass * 0.75, 0.75, 0.3);
popMatrix();
}
// Leg functionality
void displayLeg(float length, float angle, float delay) {
float rotation = 0.5 * sin(3 * (timer  delay));
float leftAngle = angle + rotation;
float rightAngle = PI  angle  rotation;
line(0, 0, length * sin(leftAngle), length * cos(leftAngle));
line(0, 0, length * sin(rightAngle), length * cos(rightAngle));
}
void update() {
// Calculate distance from the center of screen
PVector center = new PVector(width/2, height/2);
float dist = loc.dist(center);
// Check edges and bounce according to distance
if (dist >= height/2 mass) {
vel.x = vel.x * mass/4;
vel.y = vel.y * mass/4;
}
vel.add(acc);
loc.add(vel);
acc.mult(0);
vel.limit(6); // higher speed limit
}
// APPROACH to follow Boids
void approachForce(ArrayList boids) {
float count = 0; // keep track of how many boids are close
PVector locSum = new PVector(); // store locations
for (Boid other : boids) {
int approachRadius = mass + 300; // radius of distance from boids, higher
PVector dist = PVector.sub(other.getLoc(), loc);
float d = dist.mag();
if (d != 0 && d0) {
locSum.div(count); // divide by number of other positions
PVector approachVec = PVector.sub(locSum, loc);
approachVec.limit(maxForce);
applyF(approachVec);
}
}
}
