Processingで3DのBoids
Processingで3DのBoidsを作成しました。群れをつくって3D空間を飛んでいます。
基本的にはProcessingのSampleにある「Topics/Simulate/Flocking」を3Dになるように書き換えただけです。 このSampleは「The Nature of Code」の「6. AUTONOMOUS AGENTS」で解説されています。
Processing3で動作確認済みです。Processing2ではfullScreen()を使えないので、size()を使ってください。
OpenProcessingにも上げておいたので、ブラウザでも見ることができます。
Boidsはパラメータで挙動がいろいろ変わると思うので、次はGUIでパラメータを操作できるようにしたいと思います。
以下、コード。
Boids_3D.pde
Flock flock; void setup(){ fullScreen(P3D); //size(640, 640, P3D); flock = new Flock(); for(int i = 0; i < 500; i++){ flock.addBoid(new Boid(0 , 0, 0)); } } void draw(){ background(255); stroke(30); fill(255); translate(width / 2, height / 2); rotateX(map(mouseY, 0, height, -HALF_PI, HALF_PI)); rotateY(map(mouseX, 0, width, -HALF_PI, HALF_PI)); stroke(0); flock.run(); }
Boid.pde
class Boid{ PVector location; PVector velocity; PVector acceleration; float r; float maxforce; float maxspeed; Boid(float x, float y, float z){ acceleration = new PVector(0, 0, 0); float angle1 = random(PI); float angle2 = random(TWO_PI); velocity = new PVector(sin(angle1) * cos(angle2), sin(angle1) * sin(angle2), cos(angle1)); location = new PVector(x, y, z); r = 2.0; maxspeed = 2; maxforce = 0.03; } void run(ArrayList<Boid> boids){ flock(boids); update(); borders(); render(); } void applyForce(PVector force){ acceleration.add(force); } void flock(ArrayList<Boid> boids){ PVector sep = separate(boids); PVector ali = align(boids); PVector coh = cohesion(boids); sep.mult(1.5); ali.mult(1.0); coh.mult(1.0); applyForce(sep); applyForce(ali); applyForce(coh); } void update(){ velocity.add(acceleration); velocity.limit(maxspeed); location.add(velocity); acceleration.mult(0); } PVector seek(PVector target){ PVector desired = PVector.sub(target, location); desired.normalize(); desired.mult(maxspeed); PVector steer = PVector.sub(desired, velocity); steer.limit(maxforce); return steer; } void render(){ pushMatrix(); translate(location.x, location.y, location.z); rotateZ(atan2(velocity.y, velocity.x) + HALF_PI); rotateY(atan2(velocity.x, velocity.z) + HALF_PI); beginShape(TRIANGLES); vertex(0, -r * 2, 0); vertex(-r, r * 2, 0); vertex(r, r * 2, 0); endShape(); popMatrix(); } void borders(){ if(location.x > width / 2) location.x = -width / 2; if(location.y > height / 2) location.y = -height / 2; if(location.z > height / 2) location.z = -height / 2; if(location.x < -width / 2) location.x = width / 2; if(location.y < -height / 2) location.y = height / 2; if(location.z < -height / 2) location.z = height / 2; } PVector separate(ArrayList<Boid> boids){ float desiredseparation = 25.0; PVector steer = new PVector(0, 0, 0); int count = 0; for(Boid other: boids){ float d = PVector.dist(location, other.location); if((d > 0) && (d < desiredseparation)){ PVector diff = PVector.sub(location, other.location); diff.normalize(); diff.div(d); steer.add(diff); count++; } } if(count > 0){ steer.div((float)count); } if(steer.mag() > 0){ steer.normalize(); steer.mult(maxspeed); steer.sub(velocity); steer.limit(maxforce); } return steer; } PVector align(ArrayList<Boid> boids){ float neighbordist = 50; PVector sum = new PVector(0, 0, 0); int count = 0; for(Boid other: boids){ float d = PVector.dist(location, other.location); if((d > 0) && (d < neighbordist)){ sum.add(other.velocity); count++; } } if(count > 0){ sum.div((float)count); sum.normalize(); sum.mult(maxspeed); PVector steer = PVector.sub(sum, velocity); steer.limit(maxforce); return steer; } else { return new PVector(0, 0, 0); } } PVector cohesion(ArrayList<Boid> boids){ float neighbordist = 50; PVector sum = new PVector(0, 0, 0); int count = 0; for(Boid other: boids){ float d = PVector.dist(location, other.location); if((d > 0) && (d < neighbordist)){ sum.add(other.location); count++; } } if(count > 0){ sum.div(count); return seek(sum); } else{ return new PVector(0, 0, 0); } } }
Flock.pde
class Flock{ ArrayList<Boid> boids; Flock(){ boids = new ArrayList<Boid>(); } void run(){ for(Boid b: boids){ b.run(boids); } } void addBoid(Boid b){ boids.add(b); } }