xxxxxxxxxx
191
const speed = 5;
function setup() {
createCanvas(640, 480);
angleMode(DEGREES);
initializeFlock(100);
}
function draw() {
background(220);
renderFlock();
updateFlock();
}
// Boid
class Boid {
constructor() {
const theta = random(0, 360);
this.uniqueid = uid();
this.position = createVector(random(width), random(height));
this.velocity = createVector(speed * cos(theta), speed * sin(theta));
}
update() {
this.velocity
.add(cohesion(this))
.add(alignment(this))
.add(separation(this))
.limit(speed);
this.position
.add(this.velocity);
// Wrap around if the boid overshoots the canvas.
if (this.position.y < 0) this.position.y = height;
else if (this.position.y > height) this.position.y = 0;
if (this.position.x < 0) this.position.x = width;
else if (this.position.x > width) this.position.x = 0;
}
render() {
push();
translate(this.position.x, this.position.y);
rotate(this.velocity.heading());
stroke(150);
strokeWeight(1);
noFill(120);
beginShape();
vertex(10, 0);
vertex(-5, -5);
vertex(-2, 0);
vertex(-5, 5);
endShape(CLOSE);
pop();
}
equals(other) {
return this.uniqueid === other.uniqueid;
}
distance(other) {
return this.position.dist(other.position);
}
displacement(other) {
return p5.Vector.sub(other.position, this.position);
}
}
// Flock
// Stores and array of boids.
let flock = [];
// Make a flock of n boids.
// Updates the global `flock` variable.
function initializeFlock(n = 10) {
flock = [];
for (let i = 0; i < n; i++) {
flock[i] = new Boid();
}
}
// Draw one frame of the flock.
// Delegates to `Boid.render`.
function renderFlock() {
for (let i = 0; i < flock.length; i++) {
flock[i].render();
}
}
// Update all boids in the flock.
// Delegates to `Boid.update`.
function updateFlock() {
for (let i = 0; i < flock.length; i++) {
flock[i].update();
}
}
// Steering behaviors.
// Cohesion: Each boid moves towards the
// average center of the flock.
function cohesion(boid) {
let cohesionVector = createVector(0, 0);
const neighbours = getNeighbours(boid);
const averagePosition = getAveragePosition(neighbours);
if (neighbours.length != 0) {
cohesionVector =
averagePosition
.sub(boid.position)
.div(10000);
}
return cohesionVector;
}
// Alignment: Each boid aligns its velocity
// with the average velocity of the flock.
function alignment(boid) {
let alignmentVector = createVector(0, 0);
const neighbours = getNeighbours(boid);
const averageVelocity = getAverageVelocity(neighbours);
if (neighbours.length != 0) {
alignmentVector =
averageVelocity
.sub(boid.velocity)
.div(100);
}
return alignmentVector;
}
// Separation: Each boid keeps a small distance
// away from the rest of the flock.
function separation(boid) {
let separationVector = createVector(0, 0);
const neighbours = getNeighbours(boid);
for (let i = 0; i < neighbours.length; i++) {
if (boid.distance(neighbours[i]) < 20)
separationVector.sub(boid.displacement(neighbours[i]));
}
return separationVector.div(100);
}
function getNeighbours(boid) {
let neighbours = [];
for (var i = 0; i < flock.length; i++) {
if (!boid.equals(flock[i]) && boid.distance(flock[i]) < 100)
neighbours.push(flock[i])
}
return neighbours;
}
function getAveragePosition(boids) {
let averagePosition = createVector(0, 0);
for (var i = 0; i < boids.length; i++) {
averagePosition.add(boids[i].position);
}
averagePosition.div(boids.length || 1);
return averagePosition;
}
function getAverageVelocity(boids) {
let averageVelocity = createVector(0, 0);
for (var i = 0; i < boids.length; i++) {
averageVelocity.add(boids[i].velocity)
}
averageVelocity.div(boids.length || 1);
return averageVelocity;
}
// Utils
// Generate a unique id of given length (default to 7).
function uid(len = 7) {
return Math.random().toString(35).substr(2, len);
}