• fullscreen
  • agent.pde
  • boids.pde
  • controls.pde
  • object.pde
  • world.pde
  • class Agent {
      float mass;
      float energy;
      PVector pos; // Position
      PVector vel; // Velocity
      PVector acc; // Acceleration
      int type; // Agent type
      float wdelta; // Wander delta
      int action; // Current action
      int prey; // Predator's target
      // Weights
      float wArr;
      float wDep;
      float wPur;
      float wEva;
      float wWan;
      float wAvo;
      float wFlo;
      float wCoh;
      float wSep;
      float wAli;
    
      Agent(float px, float py, int t) {
        mass = 10.0;
        energy = 10*ceil(random(5, 10));
        pos = new PVector(px, py);
        vel = new PVector(random(-5, 5), random(-5, 5));
        acc = new PVector();
        type = t;
        wdelta = 0.0;
        action = 0;
        updateweight();
      }
    
      void run(ArrayList boids, ArrayList predators, ArrayList objs) {
        acc.set(0, 0, 0); // Reset accelertion to 0 each cycle
        steer(boids, predators, objs); // Update steering with approprate behavior
        vel.add(acc); // Update velocity
        switch (action) {
          case 1: vel.limit(maxpursue); break; // Limit pursue speed
          case 2: vel.limit(maxevade); break; // Limit evade speed
          default: vel.limit(maxspeed); break; // Limit speed      
        }
        pos.add(vel); // Move agent
        bounding(); // Wrap around the screen or else...
        updateweight(); // Updates weights
        render();
      }
    
      void steer(ArrayList boids, ArrayList predators, ArrayList objs) { 
        if (type == 2) predator(boids); // Determine current action
        // Initialize steering forces
        PVector arr = new PVector();
        PVector dep = new PVector();
        PVector pur = new PVector();
        PVector eva = new PVector();
        PVector wan = new PVector();
        PVector flo = new PVector();
        PVector avo = new PVector();
        // Calculate steering forces
        switch (action) {
          // Evading
          case 1: {
            eva = evade(predators);
            avo = avoid(objs); 
            break; 
          }
          // Pursuing
          case 2: {
            pur = pursue(boids);
            avo = avoid(objs);  
            break;
          }
          // Wandering
          default: {
            if (type == 1) {
              wan = wander(); 
              avo = avoid(objs);
              flo = flocking(boids);
              eva = evade(predators);
              break;
            }
            if (type == 2) {
              wan = wander(); 
              avo = avoid(objs);   
              break;
            }
          }      
        }
        // User interaction
        if (mousePressed && keyPressed == false && type == 1) {
          // Left mouse button - Arrival
          if (mouseButton == LEFT) {
            PVector mouse = new PVector(mouseX, mouseY);
            arr = arrival(mouse);
          }
          // Right mouse button - Departure
          else if (mouseButton == RIGHT) {
            PVector mouse = new PVector(mouseX, mouseY);
            dep = departure(mouse);
            dep.mult(maxevade);
          }
        }
        // Apply weights
        arr.mult(wArr);
        dep.mult(wDep);
        pur.mult(wPur);
        eva.mult(wEva);
        wan.mult(wWan);
        avo.mult(wAvo);
        flo.mult(wFlo);
        // Accumulate steering force
        acc.add(arr);
        acc.add(dep);
        acc.add(pur);
        acc.add(eva);
        acc.add(wan);
        acc.add(avo);
        acc.add(flo);
        acc.limit(maxforce); // Limit to maximum steering force
      }
      
      void predator(ArrayList boids) {
        if (energy > 0) energy -= random(0.5);
        if (energy < 0) energy = 10*ceil(random(5, 10));
        if (energy < 20 && action == 0) {
          action = 2;
          prey = int(random(boids.size() - 1));
        }        
        if (energy > 20 && action == 2) action = 0;
      }
    
      PVector seek(PVector target) {
        PVector steer; // The steering vector
        PVector desired = PVector.sub(target, pos); // A vector pointing from current location to the target
        float distance = mag2(desired); // Distance from the target is the magnitude of the vector
        // If the distance is greater than 0, calc steering (otherwise return zero vector)
        if (distance > 0) {     
          desired.normalize(); // Normalize desired
          desired.mult(maxforce);
          steer = PVector.sub(desired, vel); // Steering = Desired minus Velocity
        }
        else {
          steer = new PVector(0, 0);
        }
        return steer;
      }
    
      PVector flee(PVector target) {
        PVector steer; // The steering vector
        PVector desired = PVector.sub(target, pos); // A vector pointing from current location to the target
        float distance = mag2(desired); // Distance from the target is the magnitude of the vector
        // If the distance is greater than 0, calc steering (otherwise return zero vector)
        if (distance > 0 && distance < (ARadius*100)*(ARadius*100)) {
          desired.normalize(); // Normalize desired
          desired.mult(maxforce);
          steer = PVector.sub(vel, desired); // Steering = Desired minus Velocity
        }
        else {
          steer = new PVector(0, 0);
        }
        return steer;
      }
    
      PVector arrival(PVector target) {
        PVector steer; // The steering vector
        PVector desired = PVector.sub(target, pos); // A vector pointing from current location to the target
        float distance = mag2(desired); // Distance from the target is the magnitude of the vector
        // If the distance is greater than 0, calc steering (otherwise return zero vector)
        if (distance > 0) {
          desired.normalize(); // Normalize desired
          if (distance < ARadius*ARadius) {
            distance = (float) Math.sqrt(distance);
            desired.mult(maxspeed*(distance/ARadius)); // This damping is somewhat arbitrary
          }
          else desired.mult(maxforce);
          steer = PVector.sub(desired, vel); // Steering = Desired minus Velocity
        }
        else {
          steer = new PVector();
        }
        return steer;
      }
    
      PVector departure(PVector target) {
        PVector steer; // The steering vector
        PVector desired = PVector.sub(target, pos); // A vector pointing from current location to the target
        float distance = mag2(desired); // Distance from the target is the magnitude of the vector
        // If the distance is greater than 0, calc steering (otherwise return zero vector)
        if (distance > 0 && distance < (ARadius*100)*(ARadius*100)) {
          desired.normalize(); // Normalize desired
          if (distance < ARadius*ARadius) {
            distance = (float) Math.sqrt(distance);
            desired.mult(maxspeed*(ARadius/distance)); // This damping is somewhat arbitrary
          }
          else desired.mult(maxforce);
          steer = PVector.sub(vel, desired); // Steering = Desired minus Velocity
        }
        else {
          steer = new PVector();
        }
        return steer;
      }
      
      PVector pursue(ArrayList boids) {
        PVector steer = new PVector();
        if (prey < boids.size()) {
          Agent boid = (Agent) boids.get(prey);
          steer = PVector.sub(boid.pos, pos);
          steer.mult(maxpursue);
        }
        return steer;
      }
      
      PVector evade(ArrayList predators) {
        PVector steer = new PVector();
        for (int i = 0; i < predators.size(); i++) {
          Agent predator = (Agent) predators.get(i);
          float distance = dist2(pos, predator.pos);
          if (distance < ERadius*ERadius) {
            action = 1;
            steer = flee(predator.pos);
            steer.mult(maxevade);
            return steer;
          }
        }
        action = 0;
        return steer;
      }
    
      PVector wander() {
        wdelta += random(-NRadius, NRadius); // Determine noise ratio
        // Calculate the new location to steer towards on the wander circle
        PVector center = vel.get(); // Get center of wander circle
        center.mult(60.0); // Multiply by distance
        center.add(pos); // Make it relative to boid's location
        // Apply offset to get new target    
        PVector offset = new PVector(WRadius*cos(wdelta), WRadius*sin(wdelta));
        PVector target = PVector.add(center, offset); // Determine new target
        // Steer toward new target    
        PVector steer = seek(target); // Steer towards it    
        return steer;
      }
      
      PVector avoid(ArrayList objs) {
        PVector steer  = new PVector();    
    
        for (int i = 0; i < objs.size(); i++) {
          Obj obj = (Obj) objs.get(i);
          // Distance between object and avoidance sphere
          float distance = dist2(obj.pos, pos);
          // If distance is less than the sum of the two radius, there is collision
          float bound = obj.mass*0.5 + BRadius + ORadius;
          if (distance < bound*bound) {
            wAvo = 10.0;
            wWan = 0.1;
            float collision = (obj.mass + mass)*0.5;
            if (distance < collision*collision) {
              steer = PVector.sub(pos, obj.pos);
              steer.mult(maxforce*0.1);
              return steer;
            }
            else {
              float direction = dist2(obj.pos, PVector.add(pos, vel));
              // If is heading toward obstacle
              if (direction < distance) {
                // If steering in the verticle direction
                if (abs(vel.x) <= abs(vel.y)) {   
                  steer = new PVector((pos.x - obj.pos.x), vel.y);
                  steer.mult(maxforce*((bound*bound)/distance)*0.001);       
                }
                // If steering in the horizontal direction
                else {
                  steer = new PVector(vel.x, (pos.y - obj.pos.y));
                  steer.mult(maxforce*((bound*bound)/distance)*0.001);  
                }
              }
            }
          }
        }
        return steer;
      }
    
      PVector flocking(ArrayList boids) {
        // Get steering forces
        PVector steer = new PVector();
        PVector coh = new PVector(); // Perceived center
        PVector sep = new PVector(); // Displacement
        PVector ali = new PVector(); // Perceived velocity
        int count = 0;
        // Agents try to fly towards the centre of mass of neighbouring agents
        // Agents try to keep a small distance away from other objects (including other agents)
        // Agents try to match velocity with near agents
        for (int i = 0; i < boids.size(); i++) {
          Agent boid = (Agent) boids.get(i);
          float distance = dist2(pos, boid.pos);
          // Go through each agents
          if (this != boid && distance < Rn*Rn) {
            coh.add(boid.pos); // Cohesion
            ali.add(boid.vel); // Alignment
            count++;
          }      
          // Separation
          if (this != boid && distance < SDistance*SDistance) {
            PVector diff = PVector.sub(boid.pos, pos); // (agent.position - bJ.position)
            diff.normalize();
            distance = (float) Math.sqrt(distance);
            diff.div(distance); // Weighed by distance
            sep.sub(diff); // c = c - (agent.position - bJ.position)
          }
        }
        if (count > 0) {
          // Cohesion - Step towards the center of mass
          coh.div((float)count); // cJ = pc / (N-1)
          coh.sub(pos); // (pcJ - bJ.position)
          coh.mult(CStep); // (pcJ - bJ.position) / 100
        // Alignment - Find average velocity
          ali.div((float)count); // pvJ = pvJ / N-1
          ali.sub(vel); // (pvJ - bJ.velocity)
          ali.mult(AVelocity); // (pvJ - bJ.velocity) / 8
        }
        // Apply weights
        coh.mult(wCoh);
        sep.mult(wSep);
        ali.mult(wAli);
        // Accumulate forces
        steer.add(coh);
        steer.add(sep);
        steer.add(ali);
        // Add speed
        steer.mult(maxspeed);
        return steer;
      }
      
      // Wrap around or bounded 
      void bounding() {
        if (bounded) {
          if (pos.x <= BRadius) vel.x = BRadius - pos.x;
          else if (pos.x >= width - BRadius) vel.x = (width - BRadius) - pos.x;
          if (pos.y <= BRadius) vel.y = BRadius - pos.y;
          else if (pos.y >= height - BRadius) vel.y = (height - BRadius) - pos.y;
        }
        else {
          if (pos.x < -mass) pos.x = width + mass;
          if (pos.y < -mass) pos.y = height + mass;
          if (pos.x > width + mass) pos.x = -mass;
          if (pos.y > height + mass) pos.y = -mass;
        }
      }
      
      void updateweight() {
        wArr = KArrive;
        wDep = KDepart;
        wPur = KPursue;
        wEva = KEvade;
        wWan = KWander;
        wAvo = KAvoid;
        wFlo = KFlock;
        wCoh = KCohesion;
        wSep = KSeparate;
        wAli = KAlignment;
      }
      
      void render() {   
        if (type == 1) {
          fill(156, 206, 255);
          stroke(16, 16, 222);
          ellipse(pos.x, pos.y, mass, mass);
          PVector dir = vel.get();
          dir.normalize();
          line(pos.x, pos.y, pos.x + dir.x*10, pos.y + dir.y*10);
        }
        else if (type == 2) {
          // Draw a triangle rotated in the direction of velocity        
          float theta = vel.heading2D() + radians(90);
          pushMatrix();
          translate(pos.x, pos.y);
          rotate(theta);
          fill(220, 0, 0);
          noStroke();
          beginShape(TRIANGLES);
          vertex(0, -mass);
          vertex(-3, mass);
          vertex(3, mass);
          endShape();
          popMatrix();
        }
        // Debug mode
        if (debug) {
          // Velocity
          stroke(16, 148, 16);
          line(pos.x, pos.y, pos.x + vel.x*4, pos.y + vel.y*4);
          // Steering
          stroke(255, 0, 0);
          line(pos.x, pos.y, pos.x + acc.x*20, pos.y + acc.y*20);
          // Neighborhood radius
          fill(239, 239, 239, 10);
          stroke(132, 132, 132);
          ellipse(pos.x, pos.y, Rn*2, Rn*2);
          fill(100, 100, 100, 30);
          noStroke();
          ellipse(pos.x, pos.y, BRadius*2, BRadius*2);
          stroke(255, 0, 0);
          noFill();
        }
      }
      
      float dist2(PVector v1, PVector v2) {
        return ((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y) + (v1.z - v2.z)*(v1.z - v2.z));
      }
      
      float mag2(PVector v) {
        return (v.x*v.x + v.y*v.y + v.z*v.z);
      }
    }
    
    import controlP5.*;
    
    /* Steering Behaviors
       Future Widget Lab - futurewidgetlab.com */
       
    // Global variables
    World world;
    ControlP5 controlP5;
    
    // Setup the Processing Canvas
    void setup() {
      size(800, 600);
      world = new World();
      controlUI();
      smooth();
    }
    
    // Main draw loop
    void draw() {
      background(255);
      world.run();
    }
    
    void keyPressed() {
      // Display info
      if (key == 'a') {
        info = !info;
      }
      // Boundary mode
      if (key == 's') {
        bounded = !bounded;
        controlP5.controller("Bound").setValue((bounded) ? 1 : 0);
        redraw();
      }
      // Debug mode
      if (key == 'd') {
        debug = !debug;
        controlP5.controller("Debug").setValue((debug) ? 1 : 0);
        redraw();
      }
      // Reset
      if (key == ' ') {
        world = new World();
        redraw();
      }
    }
    
    void mousePressed() {
      mouseAction(); // Add/Delete one by one
    }
    
    void mouseDragged() {
      mouseAction(); // Add/Delete multiple
    }
    
    void mouseAction() {
      if (keyPressed) {
        if (mouseButton == LEFT) {
          // Add boid
          if (key == 'z' && bNum < 500) {
            Agent boid = new Agent(mouseX, mouseY, 1);
            world.boids.add(boid);
            bNum = world.boids.size();
            controlP5.controller("Boid Num").setValue(bNum);
          }
          else if (key == 'x' && pNum < 200) {
            Agent predator = new Agent(mouseX, mouseY, 2);
            world.predators.add(predator);
            pNum = world.predators.size();
            controlP5.controller("Predator Num").setValue(pNum);
          }
          else if (key == 'c' && oNum < 50) {
            Obj obj = new Obj(mouseX, mouseY, random(50, 100), round(random(1, 2)));
            world.objs.add(obj);
            oNum = world.objs.size();
            controlP5.controller("Object Num").setValue(oNum);
          }
        }
        if (mouseButton == RIGHT) {
          // Remove boid
          if (key == 'z') {
            if (world.boids.size() > 0) {
              world.boids.remove(0);
              bNum = world.boids.size();
              controlP5.controller("Boid Num").setValue(bNum);
            }
          }
          // Remove predator
          else if (key == 'x') {
            if (world.predators.size() > 0) {
              world.predators.remove(0);
              pNum = world.predators.size();
              controlP5.controller("Predator Num").setValue(pNum);
            }
          }
          // Remove object
          else if (key == 'c') {
            if (world.objs.size() > 0) {
              world.objs.remove(0);
              oNum = world.objs.size();
              controlP5.controller("Object Num").setValue(oNum);
            }
          }
        }
      }
    }
    
    // World
    public int bNum = 40;
    public int pNum = 2;
    public int oNum = 5;
    
    // Canvas
    public boolean info = false; // Display information
    public boolean bounded = true; // Bounded within world
    public boolean debug = false; // Display viewing fields and more
    
    // Agent
    public float Rn = 80.0; // R neighborhood
    public float maxspeed = 5.0; // Maximum speed
    public float maxforce = 3.0; // Maximum steering force
    public float maxpursue = 20.0; // Maximum steering force
    public float maxevade = 10.0; // Maximum steering force
    
    // Steering
    public float ARadius = 100.0; // Arrival: max distance at which the agent may begin to slow down
    public float ERadius = 50.0; // Evade: radius of evade range
    public float WRadius = 40.0; // Wander: radius of wandering circle
    public float NRadius = 0.3; // Wander: radius of wander noise circle
    public float BRadius = 20.0; // Avoid: radius of agent bounding sphere
    public float ORadius = 20.0; // Avoid: radius of object bounding sphere
    public float CStep = 1.0/100.0; // Cohesion: move it #% of the way towards the center
    public float SDistance = 80.0f; // Separation: small separation distance
    public float AVelocity = 1.0/8.0; // Alignment: add a small portion to the velocity
    
    // Weights
    public float KArrive = 1.0;
    public float KDepart = 1.0;
    public float KPursue = 1.0;
    public float KEvade = 1.0;
    public float KWander = 1.0;
    public float KAvoid = 5.0;
    public float KFlock = 1.0;
    public float KCohesion = 1.0;
    public float KSeparate = 2.0;
    public float KAlignment = 1.0;
    
    void controlUI() {
      // Position offsets
      int xoffset = 10;
      int yoffset = 15;
      int canvasoffset = 10;
      int worldoffset = 60;
      int agentoffset = 130;
      int steeroffset = 230;
      int flockoffset = 345;
      int weightoffset = 415;
      
      controlP5 = new ControlP5(this);
      // Group menu items
      ControlGroup ui = controlP5.addGroup("Settings", 585, 10, 215);
      ui.setBackgroundColor(color(0, 200));
      ui.setBackgroundHeight(590);
      ui.mousePressed(); // Menu is hidden by default
      // Increment/decrement stuff in the world  
      Textlabel textWorld = controlP5.addTextlabel("World", "World", xoffset, worldoffset);
      textWorld.setColorValue(color(200));
      Slider sliderBoid = controlP5.addSlider("Boid Num", 0, 500, bNum, xoffset, worldoffset + yoffset*1, 100, 10);
      Slider sliderPredator = controlP5.addSlider("Predator Num", 0, 100, pNum, xoffset, worldoffset + yoffset*2, 100, 10);
      Slider sliderObject = controlP5.addSlider("Object Num", 0, 50, oNum, xoffset, worldoffset + yoffset*3, 100, 10);
      // Canvas settings and more
      Textlabel textCanvas = controlP5.addTextlabel("Canvas", "Canvas Options", xoffset, canvasoffset);
      textCanvas.setColorValue(color(200));
      Toggle toggleInfo = controlP5.addToggle("Info", info, xoffset + 0, canvasoffset + yoffset*1, 10, 10);
      Toggle toggleBound = controlP5.addToggle("Bound", bounded, xoffset + 45, canvasoffset + yoffset*1, 10, 10);
      Toggle toggleDebug = controlP5.addToggle("Debug", debug, xoffset + 90, canvasoffset + yoffset*1, 10, 10);
      Button buttonDefault = controlP5.addButton("Default", 0, xoffset + 135, canvasoffset + yoffset*1, 45, 10);
      Button buttonReset = controlP5.addButton("Reset", 0, xoffset + 135, canvasoffset + yoffset*2, 45, 10);
      // Speed variables
      Textlabel textAgent = controlP5.addTextlabel("Agent", "Agent", xoffset, agentoffset);
      textAgent.setColorValue(color(200));
      Slider sliderRn = controlP5.addSlider("Neighborhood", 1, 200, Rn, xoffset, agentoffset + yoffset*1, 100, 10);
      Slider sliderMaxSpeed = controlP5.addSlider("Max Speed", 1, 10, maxspeed, xoffset, agentoffset + yoffset*2, 100, 10);
      Slider sliderMaxForce = controlP5.addSlider("Max Force", 1, 10, maxforce, xoffset, agentoffset + yoffset*3, 100, 10);
      Slider sliderMaxPursue = controlP5.addSlider("Pursue Speed", 1, 20, maxpursue, xoffset, agentoffset + yoffset*4, 100, 10);
      Slider sliderMaxEvade = controlP5.addSlider("Evade Speed", 1, 20, maxevade, xoffset, agentoffset + yoffset*5, 100, 10);
      // Steering variables
      Textlabel textSteer = controlP5.addTextlabel("Steering", "Steering", xoffset, steeroffset);
      textSteer.setColorValue(color(200));
      Slider sliderARadius = controlP5.addSlider("Arrival Departure", 1, 200, ARadius, xoffset, steeroffset + yoffset*1, 100, 10);
      Slider sliderERadius = controlP5.addSlider("Evasion", 1, 200, ERadius, xoffset, steeroffset + yoffset*2, 100, 10);
      Slider sliderWRadius = controlP5.addSlider("Wandering", 1, 200, WRadius, xoffset, steeroffset + yoffset*3, 100, 10);
      Slider sliderNRadius = controlP5.addSlider("Wandering Noise", 0.1, 1, NRadius, xoffset, steeroffset + yoffset*4, 100, 10);
      Slider sliderBRadius = controlP5.addSlider("Agent Avoidance", 1, 200, BRadius, xoffset, steeroffset + yoffset*5, 100, 10);
      Slider sliderORadius = controlP5.addSlider("Object Avoidance", 1, 200, ORadius, xoffset, steeroffset + yoffset*6, 100, 10);
      // Flocking variables
      Textlabel textFlock = controlP5.addTextlabel("Flocking", "Flocking", xoffset, flockoffset);
      textFlock.setColorValue(color(200));
      Slider sliderCStep = controlP5.addSlider("Cohesion Step", 0.01, 0.1, CStep, xoffset, flockoffset + yoffset*1, 100, 10);
      Slider sliderSDistance = controlP5.addSlider("Separation Distance", 1, 200, SDistance, xoffset, flockoffset + yoffset*2, 100, 10);
      Slider sliderAVelocity = controlP5.addSlider("Alignment Velocity", 0.01, 0.5, AVelocity, xoffset, flockoffset + yoffset*3, 100, 10);
      // Weight constants
      Textlabel textWeight = controlP5.addTextlabel("Weights", "Weights", xoffset, weightoffset);
      textWeight.setColorValue(color(200));
      Slider sliderKArrive = controlP5.addSlider("Arrive", 1, 10, KArrive, xoffset, weightoffset + yoffset*1, 100, 10);
      Slider sliderKDepart = controlP5.addSlider("Depart", 1, 10, KDepart, xoffset, weightoffset + yoffset*2, 100, 10);
      Slider sliderKPursue = controlP5.addSlider("Pursue", 1, 10, KPursue, xoffset, weightoffset + yoffset*3, 100, 10);
      Slider sliderKEvade = controlP5.addSlider("Evade", 1, 10, KEvade, xoffset, weightoffset + yoffset*4, 100, 10);
      Slider sliderKWander = controlP5.addSlider("Wander", 1, 10, KWander, xoffset, weightoffset + yoffset*5, 100, 10);
      Slider sliderKAvoid = controlP5.addSlider("Avoid", 1, 10, KAvoid, xoffset, weightoffset + yoffset*6, 100, 10);
      Slider sliderKFlock = controlP5.addSlider("Flock", 1, 10, KFlock, xoffset, weightoffset + yoffset*7, 100, 10);
      Slider sliderKCohesion = controlP5.addSlider("Cohesion", 1, 10, KCohesion, xoffset, weightoffset + yoffset*8, 100, 10);
      Slider sliderKSeparate = controlP5.addSlider("Separate", 1, 10, KSeparate, xoffset, weightoffset + yoffset*9, 100, 10);
      Slider sliderKAlignment = controlP5.addSlider("Alignment", 1, 10, KAlignment, xoffset, weightoffset + yoffset*10, 100, 10);
      // Assign ID to all menu items
      sliderBoid.setId(1);
      sliderPredator.setId(2);
      sliderObject.setId(3);
      toggleInfo.setId(4);
      toggleBound.setId(5);
      toggleDebug.setId(6);
      buttonDefault.setId(7);
      buttonReset.setId(8);
      sliderRn.setId(9);
      sliderMaxSpeed.setId(10);
      sliderMaxForce.setId(11);
      sliderMaxPursue.setId(12);
      sliderMaxEvade.setId(13);
      sliderARadius.setId(14);
      sliderERadius.setId(15);
      sliderWRadius.setId(16);
      sliderNRadius.setId(17);
      sliderBRadius.setId(18);
      sliderORadius.setId(19);
      sliderCStep.setId(20);
      sliderSDistance.setId(21);
      sliderAVelocity.setId(22);
      sliderKArrive.setId(23);
      sliderKDepart.setId(24);
      sliderKPursue.setId(25);
      sliderKEvade.setId(26);
      sliderKWander.setId(27);
      sliderKAvoid.setId(28);
      sliderKFlock.setId(29);
      sliderKCohesion.setId(30);
      sliderKSeparate.setId(31);
      sliderKAlignment.setId(32);  
      // Add all menu items to the UI group
      textWorld.setGroup(ui);
      sliderBoid.setGroup(ui);
      sliderPredator.setGroup(ui);
      sliderObject.setGroup(ui);  
      textCanvas.setGroup(ui);
      toggleInfo.setGroup(ui);
      toggleBound.setGroup(ui);
      toggleDebug.setGroup(ui);
      buttonDefault.setGroup(ui);
      buttonReset.setGroup(ui);  
      textAgent.setGroup(ui);
      sliderRn.setGroup(ui);
      sliderMaxSpeed.setGroup(ui);
      sliderMaxForce.setGroup(ui);
      sliderMaxPursue.setGroup(ui);
      sliderMaxEvade.setGroup(ui);  
      textSteer.setGroup(ui);
      sliderARadius.setGroup(ui);
      sliderERadius.setGroup(ui);
      sliderWRadius.setGroup(ui);
      sliderNRadius.setGroup(ui);
      sliderBRadius.setGroup(ui);
      sliderORadius.setGroup(ui);  
      textFlock.setGroup(ui);
      sliderCStep.setGroup(ui);
      sliderSDistance.setGroup(ui);
      sliderAVelocity.setGroup(ui);  
      textWeight.setGroup(ui);
      sliderKArrive.setGroup(ui);
      sliderKDepart.setGroup(ui);
      sliderKPursue.setGroup(ui);
      sliderKEvade.setGroup(ui);
      sliderKWander.setGroup(ui);
      sliderKAvoid.setGroup(ui);
      sliderKFlock.setGroup(ui);
      sliderKCohesion.setGroup(ui);
      sliderKSeparate.setGroup(ui);
      sliderKAlignment.setGroup(ui);
    }
    
    // Restore all variables to their default values
    void restoreDefault() {
      updateWorld(40, 2, 5);
      controlP5.controller("Boid Num").setValue(bNum);
      controlP5.controller("Predator Num").setValue(pNum);
      controlP5.controller("Object Num").setValue(oNum);
      info = false;
      bounded = true;
      debug = false;
      controlP5.controller("Info").setValue((info) ? 1 : 0);
      controlP5.controller("Bound").setValue((bounded) ? 1 : 0);
      controlP5.controller("Debug").setValue((debug) ? 1 : 0);
      Rn = 80.0;
      maxspeed = 5.0;
      maxforce = 3.0;
      maxpursue = 20.0;
      maxevade = 10.0;
      controlP5.controller("Neighborhood").setValue(Rn);
      controlP5.controller("Max Speed").setValue(maxspeed);
      controlP5.controller("Max Force").setValue(maxforce);
      controlP5.controller("Pursue Speed").setValue(maxpursue);
      controlP5.controller("Evade Speed").setValue(maxevade);
      ARadius = 100.0;
      ERadius = 50.0;
      WRadius = 40.0;
      NRadius = 0.3;
      BRadius = 20.0;
      ORadius = 20.0;
      CStep = 1.0/100.0;
      SDistance = 80.0f;
      AVelocity = 1.0/8.0;
      controlP5.controller("Arrival Departure").setValue(ARadius);
      controlP5.controller("Evasion").setValue(ERadius);
      controlP5.controller("Wandering").setValue(WRadius);
      controlP5.controller("Wandering Noise").setValue(NRadius);
      controlP5.controller("Agent Avoidance").setValue(BRadius);
      controlP5.controller("Object Avoidance").setValue(ORadius);
      controlP5.controller("Cohesion Step").setValue(CStep);
      controlP5.controller("Separation Distance").setValue(SDistance);
      controlP5.controller("Alignment Velocity").setValue(AVelocity);
      KArrive = 1.0;
      KDepart = 1.0;
      KPursue = 1.0;
      KEvade = 1.0;
      KWander = 1.0;
      KAvoid = 5.0;
      KFlock = 1.0;
      KCohesion = 1.0;
      KSeparate = 2.0;
      KAlignment = 1.0;
      controlP5.controller("Arrive").setValue(KArrive);
      controlP5.controller("Depart").setValue(KDepart);
      controlP5.controller("Pursue").setValue(KPursue);
      controlP5.controller("Evade").setValue(KEvade);
      controlP5.controller("Wander").setValue(KWander);
      controlP5.controller("Avoid").setValue(KAvoid);
      controlP5.controller("Flock").setValue(KFlock);
      controlP5.controller("Cohesion").setValue(KCohesion);
      controlP5.controller("Separate").setValue(KSeparate);
      controlP5.controller("Alignment").setValue(KAlignment);
    }
    
    // Update the number of agents and objects in the world
    void updateWorld(int bn, int pn, int on) {
      if (bn > bNum) {
        bNum = bn;
        while (world.boids.size() < bNum) {
          Agent boid = new Agent(random(width), random(height), 1);
          world.boids.add(boid);
        }
      }
      else if (bn < bNum) {
        bNum = bn;
        while (world.boids.size() >  bNum) {
          world.boids.remove(0);
        }    
      }
      if (pn > pNum) {
        pNum = pn;
        while (world.predators.size() < pNum) {
          Agent predator = new Agent(random(width), random(height), 2);
          world.predators.add(predator);
        }
      }
      else if (pn < pNum) {
        pNum = pn;
        while (world.predators.size() >  pNum) {
          world.predators.remove(0);
        }    
      }
      if (on > oNum) {
        oNum = on;
        while (world.objs.size() < oNum) {
          Obj obj = new Obj(random(1, width), random(1, height), random(50, 100), round(random(1, 2)));
          world.objs.add(obj);
        }
      }
      else if (on < oNum) {
        oNum = on;
        while (world.objs.size() >  oNum) {
          world.objs.remove(0);
        }    
      }
    }
    
    // Event handler
    void controlEvent(ControlEvent theEvent) {
      switch(theEvent.controller().id()) {
        case 1: { updateWorld((int)theEvent.controller().value(), pNum, oNum); break; }
        case 2: { updateWorld(bNum, (int)theEvent.controller().value(), oNum); break; }
        case 3: { updateWorld(bNum, pNum, (int)theEvent.controller().value()); break; }
        case 4: { info = (theEvent.controller().value() == 1.0) ? true : false; break; }
        case 5: { bounded = (theEvent.controller().value() == 1.0) ? true : false; break; }
        case 6: { debug = (theEvent.controller().value() == 1.0) ? true : false; break; }
        case 7: { restoreDefault(); break; }
        case 8: { world = new World(); redraw(); break; }
        case 9: { Rn = theEvent.controller().value(); break; }
        case 10: { maxspeed = theEvent.controller().value(); break; }
        case 11: { maxforce = theEvent.controller().value(); break; }
        case 12: { maxpursue = theEvent.controller().value(); break; }
        case 13: { maxevade = theEvent.controller().value(); break; }
        case 14: { ARadius = theEvent.controller().value(); break; }
        case 15: { ERadius = theEvent.controller().value(); break; }
        case 16: { WRadius = theEvent.controller().value(); break; }
        case 17: { NRadius = theEvent.controller().value(); break; }
        case 18: { BRadius = theEvent.controller().value(); break; }
        case 19: { ORadius = theEvent.controller().value(); break; }
        case 20: { CStep = theEvent.controller().value(); break; }
        case 21: { SDistance = theEvent.controller().value(); break; }
        case 22: { AVelocity = theEvent.controller().value(); break; }
        case 23: { KArrive = theEvent.controller().value(); break; }
        case 24: { KDepart = theEvent.controller().value(); break; }
        case 25: { KPursue = theEvent.controller().value(); break; }
        case 26: { KEvade = theEvent.controller().value(); break; }
        case 27: { KWander = theEvent.controller().value(); break; }
        case 28: { KAvoid = theEvent.controller().value(); break; }
        case 29: { KFlock = theEvent.controller().value(); break; }
        case 30: { KCohesion = theEvent.controller().value(); break; }
        case 31: { KSeparate = theEvent.controller().value(); break; }
        case 32: { KAlignment = theEvent.controller().value(); break; }
      }
    }
    
    class Obj {
      PVector pos;
      float mass;
      int type;
    
      Obj(float px, float py, float m, int t) {
        pos = new PVector(px, py);
        mass = m;
        type = t;
      }
    }
    
    class World {
      ArrayList boids;
      ArrayList predators;
      ArrayList objs;
    
      World() {
        initAgents();
        initobjs();
      }
    
      void initAgents() {
        // Add boids
        boids = new ArrayList();
        for (int i = 0; i < bNum; i++) {
          Agent boid = new Agent(random(width), random(height), 1);
          boids.add(boid);
        }
        // Add predator
        predators = new ArrayList();
        for (int i = 0; i < pNum; i++) {
          Agent predator = new Agent(random(width), random(height), 2);
          predators.add(predator);
        }
      }
    
      void initobjs() {
        objs = new ArrayList();
        // Add objects
        for (int i = 0; i < oNum; i++) {
          Obj obj = new Obj(random(1, width), random(1, height), random(50, 100), round(random(1, 2)));
          objs.add(obj);
        }
      }
    
      void run() {
        update();
        render();
      }
    
      void update() {
        // Update agents
        for (int i = 0; i < boids.size(); i++) {
          Agent boid = (Agent) boids.get(i);
          boid.run(boids, predators, objs);
        }
        for (int i = 0; i < predators.size(); i++) {
          Agent predator = (Agent) predators.get(i);
          predator.run(boids, predators, objs);
        }
      }
    
      void render() {
        // Render objects
        for (int i = 0; i < objs.size(); i++) {
          Obj obj = (Obj) objs.get(i);
    
          if (obj.type == 1) {
            fill(200, 180, 160);
            stroke(50, 30, 20);
            ellipse(obj.pos.x, obj.pos.y, obj.mass, obj.mass);
          }
          else if (obj.type == 2) {
            fill(120, 190, 150);
            stroke(80, 70, 40);
            ellipse(obj.pos.x, obj.pos.y, obj.mass, obj.mass);
          }
          // Debug mode
          if (debug) {
            // Neighborhood radius
            fill(100, 100, 100, 30);
            noStroke();
            ellipse(obj.pos.x, obj.pos.y, obj.mass + ORadius*2, obj.mass + ORadius*2);
          }
        }
        // Render info
        if (info) {
          fill(0);
          text("FPS: " + frameRate, 15, 20);
          text("Boids: " + (world.boids.size()), 15, 35);
          text("Predators: " + (world.predators.size()), 15, 50);
          text("Objects: " + (world.objs.size()), 15, 65);
        }
      }
    }
    

    code

    tweaks (0)

    about this sketch

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

    license

    advertisement


    gum

    Steering Behaviors

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

    2D simulation of flocking and other steering behaviors.

    Instruction:
    'alt + h' - show/hide settings menu
    'alt + left mouse' - move settings menu
    'a' - display information
    's' - enable/disable bounded canvas
    'd' - debug mode
    'space' - reset
    'z/x/c + left/right mouse' - add/delete boid/predators/objects

    References:
    - http://www.red3d.com/cwr/steer/
    - http://www.vergenet.net/~conrad/boids/pseudocode.html
    - http://www.shiffman.net/teaching/nature/steering/

    Todo:
    - Code optimization
    - Different types of objects

    Jonathan Chemla
    18 Aug 2010
    You really make a great stuff :)
    It's just simple, but beautifull graphics and render.
    I tried to made one, it just failed a bit, and your sketch is really great :)
    Thanks, I'll soon try to make mine better :)

    Really beautifull one
    gum
    19 Aug 2010
    Glad you like it and thanks for the compliment, feel free to give any suggestions/feedbacks for future improvements as well =)
    hello..
    im new here..=)
    so far i really want to know how exactly your algorithm to make steering behavioral of obstacle avoidance...
    please help me to get one simple program of obstacle avoidance because i saw in your sketch there has implement it on agent.
    please sir help me to making my final project work..huhu
    seems i use Microsoft visual C++ and OpenGL in my program, so i really need the formula or theory to make it work.
    aka olsononaz
    3 Dec 2010
    fantastic job!
    I do not code myself (a friend gave me the Processing adress) but this is the nicest thing I've seen so far. The great pleasure is to change the settings to see what happen. And changes are realy strong. It could become a great game. If you imagine that the wolves (let's call them wolves) can reproduce, can eat sheeps and need to eat sheeps not to die, and that sheep can reproduce, the game could be, (once you have choozen to be wolves or sheeps) to find the appropriated parameters to:
    - get rid of wolves
    - eat all the sheeps and die
    - keep the best balance to have the maximum of animals together
    - having the maximum of wolves without killing every one
    - having the minimum of wolves without having them disapearing
    the easiest parameters could maybe not be changed, like speed or so. But it could realy become a great practical exercise about group "efficiancy" (sorry but english isn't my main language...): when individualism is a force or a weakness, when a group is more a problem than a solution etc.
    keep up the good job, and thanks a lot for the good time.
    I send this page to coding friends.
    there is a video tutorial by Jose Sanchez, showing basics of steering behaviors:
    http://www.vimeo.com/19211448
    Mawh
    11 Dec 2011
    Great work !
    You need to login/register to comment.