xxxxxxxxxx
215
let manager;
function setup() {
createCanvas(400, 300);
// noLoop();
manager = new boidManager(50, 8, 40);
}
function draw() {
background(0);
manager.update();
manager.show();
}
class boidManager {
constructor(num, avoidRange, alignRange) {
this.num = num;
this.avoidRange = avoidRange;
this.alignRange = alignRange;
this.avoidFactor = 0.05;
this.alignFactor = 0.05;
this.centreFactor = 0.0005;
this.turnFactor = 0.2;
this.margin = 20;
this.maxSpeed = 1;
this.minSpeed = 0.125;
this.chunkN = 20;
this.chunkW = width/this.chunkN;
this.chunkH = height/this.chunkN;
this.randomBoids();
}
randomBoids() {
this.chunks = new Map();
this.boids = [];
for(let i = 0; i < this.num; i ++) {
let b = new Boid(i);
this.boids.push(b);
this.addBoidToChunk(b);
}
console.log(this.chunks);
}
addBoidToChunk(boid) {
const chunkId = this.getChunkId(boid);
const chunkList = this.getBoidsForChunk(this.chunks, chunkId);
chunkList.push(boid.id);
this.chunks.set(chunkId, chunkList);
}
update() {
const newChunks = new Map();
for(let i = 0; i < this.num; i ++) {
const b = this.boids[i];
let avoidDelta = createVector(0, 0);
let avgVel = createVector(0, 0);
let avgPos = createVector(0, 0);
let alignNum = 0;
const nearby = this.getNearbyBoids(b);
for(let j = 0; j < nearby.length; j ++) {
if(nearby[j] == b.id) {
continue;
}
const other = this.boids[nearby[j]];
let avoid = false;
if(other.pos.dist(b.pos) < this.avoidRange) {
avoidDelta.x += b.pos.x - other.pos.x;
avoidDelta.y += b.pos.y - other.pos.y;
avoid = true;
}
if(!avoid && other.pos.dist(b.pos) < this.alignRange) {
avgPos.add(other.pos);
avgVel.add(other.vel);
alignNum += 1;
}
}
avoidDelta.mult(this.avoidFactor);
b.vel.add(avoidDelta);
if(alignNum !== 0) {
avgPos.div(alignNum);
avgVel.div(alignNum);
avgPos.sub(b.pos);
avgVel.sub(b.vel);
avgPos.mult(this.alignFactor);
avgVel.mult(this.centreFactor);
b.vel.add(avgVel);
b.vel.add(avgPos);
}
if(b.pos.x < this.margin) {
b.vel.x += this.turnFactor;
}
if(b.pos.x > width - this.margin) {
b.vel.x -= this.turnFactor;
}
if(b.pos.y < this.margin) {
b.vel.y += this.turnFactor;
}
if(b.pos.y > height - this.margin) {
b.vel.y -= this.turnFactor;
}
if(b.vel.magSq() < this.minSpeed * this.minSpeed) {
b.vel.setMag(this.minSpeed);
}
b.vel.limit(this.maxSpeed);
b.pos.add(b.vel);
const newChunkId = this.getChunkId(b);
const chunkList = this.getBoidsForChunk(newChunks, newChunkId);
chunkList.push(b.id);
newChunks.set(newChunkId, chunkList);
}
this.chunks = newChunks;
}
show() {
push();
noFill();
stroke(255);
for(let i = 0; i < this.num; i ++) {
let pos = this.boids[i].pos;
point(pos.x, pos.y);
}
pop();
}
getNearbyBoids(boid) {
const chunk = this.getChunk(boid);
const x = chunk.x;
const y = chunk.y;
let boids = [];
for(let i = -1; i <= 1; i ++) {
for(let j = -1; j <= 1; j ++) {
const chunkId = this.getChunkIdForChunk(createVector(x + i, y + j));
const chunkBoids = this.getBoidsForChunk(this.chunks, chunkId);
boids = boids.concat(chunkBoids);
}
}
return boids;
}
getBoidsForChunk(chunkMap, chunkId) {
let chunkList = chunkMap.get(chunkId);
if(!chunkList) {
chunkList = [];
}
return chunkList;
}
getChunk(boid) {
const x = floor(boid.pos.x/this.chunkW);
const y = floor(boid.pos.y/this.chunkH);
return createVector(x, y);
}
getChunkId(boid) {
const chunk = this.getChunk(boid);
return this.getChunkIdForChunk(chunk);
}
getChunkIdForChunk(chunk) {
return `${chunk.x}_${chunk.y}`;
}
}
class Boid {
constructor(id) {
this.id = id;
this.pos = createVector(random(width), random(height));
this.vel = p5.Vector.random2D();
}
}