/* built with Studio Sketchpad:
* https://sketchpad.cc
*
* observe the evolution of this sketch:
* https://studio.sketchpad.cc/sp/pad/view/ro.4SZdV9t0CRg/rev.4370
*
* authors:
* Jason Reich
*
* license (unless otherwise specified):
* creative commons attribution-share alike 3.0 license.
* https://creativecommons.org/licenses/by-sa/3.0/
*/
// Pressing Control-R will render this sketch.
// Click to add human targets
ArrayList flock = new ArrayList();
float personal_space = 30;
float zombie_sight = 50;
float human_sight = 75;
float zombie_maxacc = 0.1;
float human_maxacc = 0.5;
float zombie_maxspeed = 1;
float human_maxspeed = 2;
int max_life = 100;
void setup() { // this is run once.
// canvas size (Variable aren't evaluated. Integers only, please.)
size(500, 500);
// smooth edges
smooth();
// limit the number of frames per second
frameRate(30);
// set the width of the line.
strokeWeight(3);
for(int i = 0; i < 50; i++) {
Boid b = new Boid( new PVector(random(width), random(height))
, new PVector(random(-0.1,0.1), random(-0.1,0.1)), true);
flock.add(b);
}
println("Green rings are zombies. White rings are prey.");
println("Click to add a human target at that location.");
println("The centre of a human ring gets more green as they get closer to becoming zombied.");
}
void draw() { // this is run repeatedly.
background(50);
for(int i = 0; i < flock.size(); i++) {
Boid b = (Boid) flock.get(i);
b.draw();
b.step();
}
for(int i = 0; i < flock.size(); i++) {
Boid b = (Boid) flock.get(i);
b.flip();
}
}
void mousePressed() {
flock.add(new Boid( new PVector(mouseX, mouseY)
, new PVector(random(-0.1,0.1), random(-0.1,0.1)), false));
}
class Boid {
PVector position, velocity, next_position, next_velocity;
boolean undead;
int life;
Boid(PVector pos, PVector vel, boolean und) {
next_position = position = pos;
next_velocity = velocity = vel;
undead = und;
life = max_life;
}
float sight() {
return undead ? zombie_sight : human_sight;
}
void flip() {
position = next_position;
velocity = next_velocity;
if( !undead && life <= 0 ) undead = true;
}
void draw() {
if(undead) {
noFill();
stroke(0, 255, 0, 255);
} else {
fill(0, 255, 0, 255 * (max_life - life) / max_life);
stroke(255, 255, 255, 255);
}
ellipse(position.x-5, position.y-5, 10,10);
}
ArrayList calcNeighbourhood() {
ArrayList n = new ArrayList();
for(int i = 0; i < flock.size(); i++) {
Boid b = (Boid) flock.get(i);
if( PVector.dist(position, b.position) <= sight() ) {
n.add(b);
}
}
return n;
}
PVector stumble() {
PVector v = new PVector(random(-1, 1), random(-1, 1));
v.normalize();
v.mult(velocity.mag() / 100);
return v;
}
PVector separation(ArrayList n) {
PVector v = new PVector();
float count = 0;
for(int i = 0; i < n.size(); i++) {
Boid b = n.get(i);
PVector diff = PVector.sub(position, b.position);
float m = diff.mag();
if(m > 0 && m <= personal_space && !(undead && !(b.undead))) {
diff.normalize();
diff.div(m);
v.add(diff);
count = count + 1;
};
}
if(count > 0) v.div(count);
return v;
}
PVector prey(ArrayList n) {
PVector v = new PVector();
if( !undead ) {
for(int i = 0; i < n.size(); i++) {
Boid b = n.get(i);
PVector diff = PVector.sub(position, b.position);
float m = diff.mag();
if(m > 0 && b.undead) {
diff.normalize();
diff.div(m);
v.add(diff);
if(m <= personal_space) life -= 2;
};
}
if( life < max_life ) life++;
}
return v;
}
PVector predator(ArrayList n) {
PVector v = new PVector();
if( undead ) {
PVector nearest;
float nearest_dist = 1 + zombie_sight;
for(int i = 0; i < n.size(); i++) {
Boid b = n.get(i);
float b_dist = PVector.sub(b.position, position).mag();
if( !(b.undead) && b_dist < nearest_dist ) {
nearest = b.position;
nearest_dist = b_dist;
}
}
if( nearest != null ) {
v = PVector.sub(nearest, PVector.add(position, velocity));
}
}
return v;
}
void step() {
ArrayList n = calcNeighbourhood();
PVector next_acc = new PVector(0, 0);
next_acc.add(separation(n));
next_acc.add(predator(n));
next_acc.add(stumble());
next_acc.add(prey(n));
next_acc.limit(undead ? zombie_maxacc : human_maxacc);
next_velocity = PVector.add(velocity, next_acc);
next_velocity.limit(undead ? zombie_maxspeed : human_maxspeed);
next_position = PVector.add(position, next_velocity);
float bufferzone = 10;
float xmin = 0 - bufferzone;
float xmax = width + bufferzone;
float ymin = 0 - bufferzone;
float ymax = height + bufferzone;
if( next_position.x < xmin ) next_position.x = xmax - (xmin - next_position.x);
if( next_position.x > xmax ) next_position.x = xmin - (xmax - next_position.x);
if( next_position.y < ymin ) next_position.y = ymax - (ymin - next_position.y);
if( next_position.y > ymax ) next_position.y = ymin - (ymax - next_position.y);
}
}