Simulating flocking behavior

Flocking is a term applied to the behavior of birds and other flying animals that are organized into a swarm or flock.

From our point of view, it is especially interesting that flocking behavior can be simulated by applying only three rules to each particle (Boid). These rules are as follows:

  • Separation: Avoid neighbors that are too near
  • Alignment: Steer towards the average velocity of neighbors
  • Cohesion: Steer towards the average position of neighbors

Getting ready

In this recipe, we are going to use the code from the Creating a particle system in 2D recipe.

How to do it…

We will implement the rules for flocking behavior. Perform the following steps to do so:

  1. Change the number of the particles, their radius, and mass.
    int numParticle = 50;
    float radius = 5.f;
    float mass = 1.f;
  2. Add a definition for new methods and properties to the Particle class inside the Particle.h header file.
    void flock(std::vector<Particle*>& particles);
    ci::Vec2f steer(ci::Vec2f target, bool slowdown);
    void borders(float width, float height);
    ci::Vec2f separate(std::vector<Particle*>& particles);
    ci::Vec2f align(std::vector<Particle*>& particles);
    ci::Vec2f cohesion(std::vector<Particle*>& particles);
    
    float maxspeed;
    float maxforce;
    ci::Vec2f vel;
  3. Set the default values for maxspeed and maxforce at the end of the Particle constructor inside the Particle.cpp source file.
    this->maxspeed = 3.f;
    this->maxforce = 0.05f;
  4. Implement the new methods of the Particle class inside the Particle.cpp source file.
    void Particle::flock(std::vector<Particle*>& particles) {
      ci::Vec2f acc;
      acc += separate(particles) * 1.5f;
      acc += align(particles) * 1.0f;
      acc += cohesion(particles) * 1.0f;
      vel += acc;
      vel.limit(maxspeed);
    }
    
    ci::Vec2f Particle::steer(ci::Vec2f target, bool slowdown) {
    ci::Vec2f steer;
    ci::Vec2f desired = target - position;
    float d = desired.length();
    if (d >0) {
      desired.normalize();
      if ((slowdown) && (d <100.0)) desired *= (maxspeed*(d/100.0));
      else desired *= maxspeed;
      steer = desired - vel;
      steer.limit(maxforce);
        }
    else {
      steer = ci::Vec2f::zero();
        }
      return steer;
    }
    
    void Particle::borders(float width, float height) {
      if (position.x< -radius) position.x = width+radius;
      if (position.y< -radius) position.y = height+radius;
      if (position.x>width+radius) position.x = -radius;
      if (position.y>height+radius) position.y = -radius;
    }
  5. Add a method for the separation rule.
    ci::Vec2f Particle::separate(std::vector<Particle*>& particles) {
    ci::Vec2f resultVec = ci::Vec2f::zero();
    float targetSeparation = 30.f;
    int count = 0;
    for( std::vector<Particle*>::iterator it = particles.begin(); it != particles.end(); ++it ) {
      ci::Vec2f diffVec = position - (*it)->position;
      if( diffVec.length() >0&&diffVec.length() <targetSeparation ) {
        resultVec += diffVec.normalized() / diffVec.length();
        count++;
            }
        }
    
    if (count >0) {
      resultVec /= (float)count;
        }
    
    if (resultVec.length() >0) {
      resultVec.normalize();
      resultVec *= maxspeed;
      resultVec -= vel;
      resultVec.limit(maxforce);
        }
    
    return resultVec;
    }
  6. Add a method for the alignment rule.
    ci::Vec2f Particle::align(std::vector<Particle*>& particles) {
    ci::Vec2f resultVec = ci::Vec2f::zero();
    float neighborDist = 50.f;
    int count = 0;
    for( std::vector<Particle*>::iterator it = particles.begin(); it != particles.end(); ++it ) {
    ci::Vec2f diffVec = position - (*it)->position;
    if( diffVec.length() >0 && diffVec.length() <neighborDist ) {
    resultVec += (*it)->vel;
    count++;
            }
        }
    
    if (count >0) {
      resultVec /= (float)count;
    }
    
      if (resultVec.length() >0) {
      resultVec.normalize();
      resultVec *= maxspeed;
      resultVec -= vel;
      resultVec.limit(maxforce);
        }
    
      return resultVec;
    }
  7. Add a method for the cohesion rule.
    ci::Vec2f Particle::cohesion(std::vector<Particle*>& particles) {
    ci::Vec2f resultVec = ci::Vec2f::zero();
    float neighborDist = 50.f;
    int count = 0;
    for( std::vector<Particle*>::iterator it = particles.begin(); it != particles.end(); ++it ) {
      float d = position.distance( (*it)->position );
      if( d >0 && d <neighborDist ) {
        resultVec += (*it)->position;
        count++;
            }
        }
    
    if (count >0) {
      resultVec /= (float)count;
      return steer(resultVec, false);
        }
    
      return resultVec;
    }
  8. Change the update method to read as follows
    void Particle::update(){
      ci::Vec2f temp = position;
      position += vel + forces / mass;
      prevPosition = temp;
      forces = ci::Vec2f::zero();
    }
  9. Change the drawing method of Particle, as follows:
    void Particle::draw(){
      ci::gl::color(1.f, 1.f, 1.f);
      ci::gl::drawSolidCircle( position, radius );
      ci::gl::color(1.f, 0.f, 0.f);
      ci::gl::drawLine(position,
      position+( position - prevPosition).normalized()*(radius+5.f) );
    }
  10. Change the update method of ParticleSystem inside the ParticleSystem.cpp source file, as follows:
    void ParticleSystem::update(){
      for( std::vector<Particle*>::iterator it = particles.begin(); it!= particles.end(); ++it ){
            (*it)->flock(particles);
            (*it)->update();
            (*it)->borders(640.f, 480.f);
        }
    }

How it works…

Three rules for flocking—separation, alignment, and cohesion—were implemented starting from step 4 and they were applied to each particle in step 10. In this step, we also prevented Boids from going out of the window boundaries by resetting their positions.

How it works…
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.160.43