• fullscreen
  • Boid.pde
  • BoidList.pde
  • flock3d.pde
  • /*Boid object class
    * Matt Wetmore
    * Changelog
    * ---------
    * 12/14/09: Started work
    * 12/16/09: Revised code to work with forces instead of heading. First steering algorithm implemented
    * 12/18/09: Arrival steering behavior
    * 12/20/09: Alignment added NEED TO FIX COHESION
    * 1/6/10: Finished algorithms. Time to clean up the code!
    */
    
    class Boid
    {
      //fields
      PVector pos,vel,acc,ali,coh,sep; //pos, velocity, and acceleration in a vector datatype
      float neighborhoodRadius; //radius in which it looks for fellow boids
      float maxSpeed = 4; //maximum magnitude for the velocity vector
      float maxSteerForce = .1; //maximum magnitude of the steering vector
      float h; //hue
      float sc=3; //scale factor for the render of the boid
      float flap = 0;
      float t=0;
      boolean avoidWalls = false;
      
      //constructors
      Boid(PVector inPos)
      {
        pos = new PVector();
        pos.set(inPos);
        vel = new PVector(random(-1,1),random(-1,1),random(1,-1));
        acc = new PVector(0,0,0);
        neighborhoodRadius = 100;
      }
      Boid(PVector inPos,PVector inVel,float r)
      {
        pos = new PVector();
        pos.set(inPos);
        vel = new PVector();
        vel.set(inVel);
        acc = new PVector(0,0);
        neighborhoodRadius = r;
      }
      
      void run(ArrayList bl)
      {
        t+=.1;
        flap = 10*sin(t);
        //acc.add(steer(new PVector(mouseX,mouseY,300),true));
        //acc.add(new PVector(0,.05,0));
        if(avoidWalls)
        {
          acc.add(PVector.mult(avoid(new PVector(pos.x,height,pos.z),true),5));
          acc.add(PVector.mult(avoid(new PVector(pos.x,0,pos.z),true),5));
          acc.add(PVector.mult(avoid(new PVector(width,pos.y,pos.z),true),5));
          acc.add(PVector.mult(avoid(new PVector(0,pos.y,pos.z),true),5));
          acc.add(PVector.mult(avoid(new PVector(pos.x,pos.y,300),true),5));
          acc.add(PVector.mult(avoid(new PVector(pos.x,pos.y,900),true),5));
        }
        flock(bl);
        move();
        checkBounds();
        render();
      }
      
      /////-----------behaviors---------------
      void flock(ArrayList bl)
      {
        ali = alignment(bl);
        coh = cohesion(bl);
        sep = seperation(bl);
        acc.add(PVector.mult(ali,1));
        acc.add(PVector.mult(coh,3));
        acc.add(PVector.mult(sep,1));
      }
      
      void scatter()
      {
        
      }
      ////------------------------------------
        
      void move()
      {
        vel.add(acc); //add acceleration to velocity
        vel.limit(maxSpeed); //make sure the velocity vector magnitude does not exceed maxSpeed
        pos.add(vel); //add velocity to position
        acc.mult(0); //reset acceleration
      }
      
      void checkBounds()
      {
        if(pos.x>width) pos.x=0;
        if(pos.x<0) pos.x=width;
        if(pos.y>height) pos.y=0;
        if(pos.y<0) pos.y=height;
        if(pos.z>900) pos.z=300;
        if(pos.z<300) pos.z=900;
      }
      
      void render()
      {
        
        pushMatrix();
        translate(pos.x,pos.y,pos.z);
        rotateY(atan2(-vel.z,vel.x));
        rotateZ(asin(vel.y/vel.mag()));
        stroke(h);
        noFill();
        noStroke();
        fill(h);
        //draw bird
        beginShape(TRIANGLES);
        vertex(3*sc,0,0);
        vertex(-3*sc,2*sc,0);
        vertex(-3*sc,-2*sc,0);
        
        vertex(3*sc,0,0);
        vertex(-3*sc,2*sc,0);
        vertex(-3*sc,0,2*sc);
        
        vertex(3*sc,0,0);
        vertex(-3*sc,0,2*sc);
        vertex(-3*sc,-2*sc,0);
        
        /* wings
        vertex(2*sc,0,0);
        vertex(-1*sc,0,0);
        vertex(-1*sc,-8*sc,flap);
        
        vertex(2*sc,0,0);
        vertex(-1*sc,0,0);
        vertex(-1*sc,8*sc,flap);
        */
        
        vertex(-3*sc,0,2*sc);
        vertex(-3*sc,2*sc,0);
        vertex(-3*sc,-2*sc,0);
        endShape();
        //box(10);
        popMatrix();
      }
      
      //steering. If arrival==true, the boid slows to meet the target. Credit to Craig Reynolds
      PVector steer(PVector target,boolean arrival)
      {
        PVector steer = new PVector(); //creates vector for steering
        if(!arrival)
        {
          steer.set(PVector.sub(target,pos)); //steering vector points towards target (switch target and pos for avoiding)
          steer.limit(maxSteerForce); //limits the steering force to maxSteerForce
        }
        else
        {
          PVector targetOffset = PVector.sub(target,pos);
          float distance=targetOffset.mag();
          float rampedSpeed = maxSpeed*(distance/100);
          float clippedSpeed = min(rampedSpeed,maxSpeed);
          PVector desiredVelocity = PVector.mult(targetOffset,(clippedSpeed/distance));
          steer.set(PVector.sub(desiredVelocity,vel));
        }
        return steer;
      }
      
      //avoid. If weight == true avoidance vector is larger the closer the boid is to the target
      PVector avoid(PVector target,boolean weight)
      {
        PVector steer = new PVector(); //creates vector for steering
        steer.set(PVector.sub(pos,target)); //steering vector points away from target
        if(weight)
          steer.mult(1/sq(PVector.dist(pos,target)));
        //steer.limit(maxSteerForce); //limits the steering force to maxSteerForce
        return steer;
      }
      
      PVector seperation(ArrayList boids)
      {
        PVector posSum = new PVector(0,0,0);
        PVector repulse;
        for(int i=0;i<boids.size();i++)
        {
          Boid b = (Boid)boids.get(i);
          float d = PVector.dist(pos,b.pos);
          if(d>0&&d<=neighborhoodRadius)
          {
            repulse = PVector.sub(pos,b.pos);
            repulse.normalize();
            repulse.div(d);
            posSum.add(repulse);
          }
        }
        return posSum;
      }
      
      PVector alignment(ArrayList boids)
      {
        PVector velSum = new PVector(0,0,0);
        int count = 0;
        for(int i=0;i<boids.size();i++)
        {
          Boid b = (Boid)boids.get(i);
          float d = PVector.dist(pos,b.pos);
          if(d>0&&d<=neighborhoodRadius)
          {
            velSum.add(b.vel);
            count++;
          }
        }
        if(count>0)
        {
          velSum.div((float)count);
          velSum.limit(maxSteerForce);
        }
        return velSum;
      }
      
      PVector cohesion(ArrayList boids)
      {
        PVector posSum = new PVector(0,0,0);
        PVector steer = new PVector(0,0,0);
        int count = 0;
        for(int i=0;i<boids.size();i++)
        {
          Boid b = (Boid)boids.get(i);
          float d = dist(pos.x,pos.y,b.pos.x,b.pos.y);
          if(d>0&&d<=neighborhoodRadius)
          {
            posSum.add(b.pos);
            count++;
          }
        }
        if(count>0)
        {
          posSum.div((float)count);
        }
        steer = PVector.sub(posSum,pos);
        steer.limit(maxSteerForce); 
        return steer;
      }
    }
    
    /*BoidList object class
    * Matt Wetmore
    * Changelog
    * ---------
    * 12/18/09: Started work
    */
    
    class BoidList
    {
      ArrayList boids; //will hold the boids in this BoidList
      float h; //for color
      
      BoidList(int n,float ih)
      {
        boids = new ArrayList();
        h = ih;
        for(int i=0;i<n;i++)
          boids.add(new Boid(new PVector(width/2,height/2,600)));
      }
      
      void add()
      {
        boids.add(new Boid(new PVector(width/2,height/2)));
      }
      
      void addBoid(Boid b)
      {
        boids.add(b);
      }
      
      void run(boolean aW)
      {
        for(int i=0;i<boids.size();i++) //iterate through the list of boids
        {
          Boid tempBoid = (Boid)boids.get(i); //create a temporary boid to process and make it the current boid in the list
          tempBoid.h = h;
          tempBoid.avoidWalls = aW;
          tempBoid.run(boids); //tell the temporary boid to execute its run method
        }
      }
    }
    
    /*Main runner area
    * Matt Wetmore
    * Changelog
    * ---------
    * 12/14/09: Started work
    * 12/18/09: Reimplemented with BoidList class
    */
    
    int initBoidNum = 300; //amount of boids to start the program with
    BoidList flock1;//,flock2,flock3;
    float zoom=800;
    boolean smoothEdges = false,avoidWalls = false;
    
    
    
    void setup()
    {
      size(800,600,P3D);
      //create and fill the list of boids
      flock1 = new BoidList(initBoidNum,255);
      //flock2 = new BoidList(100,255);
      //flock3 = new BoidList(100,128);
    }
    
    void draw()
    {
      //clear screen
      beginCamera();
      camera();
      rotateX(map(mouseY,0,height,0,TWO_PI));
      rotateY(map(mouseX,width,0,0,TWO_PI));
      translate(0,0,zoom);
      endCamera();
      background(205);
      directionalLight(255,255,255, 0, 1, -100); 
      noFill();
      stroke(0);
      
      line(0,0,300,  0,height,300);
      line(0,0,900,  0,height,900);
      line(0,0,300,  width,0,300);
      line(0,0,900,  width,0,900);
      
      line(width,0,300,  width,height,300);
      line(width,0,900,  width,height,900);
      line(0,height,300,  width,height,300);
      line(0,height,900,  width,height,900);
      
      line(0,0,300,  0,0,900);
      line(0,height,300,  0,height,900);
      line(width,0,300,  width,0,900);
      line(width,height,300,  width,height,900);
      
      flock1.run(avoidWalls);
      //flock2.run();
      //flock3.run();
      if(smoothEdges)
        smooth();
      else
        noSmooth();
    }
    
    void keyPressed()
    {
      switch (keyCode)
      {
        case UP: zoom-=10; break;
        case DOWN: zoom+=10; break;
      }
      switch (key)
      {
        case 's': smoothEdges = !smoothEdges; break;
        case 'a': avoidWalls = !avoidWalls; break;
      }
    }
    

    code

    tweaks (0)

    about this sketch

    This sketch is running as Java applet, exported from Processing.

    license

    advertisement

    Matthew Wetmore

    3D Flocking Boids II

    Add to Faves Me Likey@! 11
    You must login/register to add this sketch to your favorites.

    UP and DOWN to zoom in and out. Press 'a' to toggle wall avoid and 's' to toggle antialiasing (smoothing). AA will slow down the sketch!

    Updated with 3D rotations and pyramid-shaped boids.

    subpixel
    13 Mar 2010
    Hey Matthew I'd like to contact you about this sketch. Please drop me an email. You can find me at subpixels.com

    -spxl
    leo garcia
    5 Oct 2013
    nice, very nice, I saw it in "The Wilderness Downtown"
    You need to login/register to comment.