Tutorials | (back to the list of tutorials) |
Each of those three rules has different distance range. Typically cohesion has the largest range, alignment has the second largest and the separation has the smallest, but depending on the intended behaviors of swarm agents, this order can be changed.
For more description about the algorithm of boids, please see Craig Reynolds's boids page .
Other agents are counted as neighbor agents if the distance to the agent is less than the threshold distance of cohesion (cohesionDist). The center of neighbors is calculated by adding all position of them and then divided by the number of the neighbors (count). The force is calculated by taking difference vector between the current agent and the center. The amount of the force is adjusted by the coefficient cohesionRatio.
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(30); for(int i=0; i < 100; i++){ new MyBoid(IRand.pt(100,100,0), IRand.pt(-10,-10,0,10,10,0)).clr(IRand.clr()); } } class MyBoid extends IParticle{ double cohesionDist = 50; double cohesionRatio = 5; IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void interact(ArrayList< IDynamics > agents){ IVec center = new IVec(); //zero vector int count = 0; for(int i=0; i < agents.size(); i++){ if(agents.get(i) instanceof MyBoid){ MyBoid b = (MyBoid)agents.get(i); if(b != this){ if(b.pos().dist(pos()) < cohesionDist){ center.add(b.pos()); count++; } } } } if(count > 0){ center.div(count); //center of neighbors IVec force = center.sub(pos()); //difference of position push(force.mul(cohesionRatio)); } } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }
In the same way with cohesion rule,
the threshold distance of separation (separationDist)
is used to determine if
the other agent is close enough to
get away from.
The force is calculated by a summantion
of each force from close agents.
The amount individual force is reset by
this formula
force.len( separationDist - dist )
to make the closer agent's force larger and
farther force smaller (and the agent at the distance of separationDist,
the force is zero).
The total force for the current agent to move away
(separationForce) is adjusted by the
coefficient separationRatio.
There is one additional note about this conditional statement dist != 0 in the in the if-condition. It is to avoid the case when the two agents' positions are identical and the direction to push out the agent cannot be defined from the difference. To be more precise, the agent should be pushed out in some default direction when they are in a same position but in the following example, this case is just excluded to keep it simple.
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(60); for(int i=0; i < 100; i++){ new MyBoid(IRand.pt(100,100,0), IRand.pt(-5,-5,0,20,20,0)).clr(IRand.clr()); } } class MyBoid extends IParticle{ double separationDist = 30; double separationRatio = 5; IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void interact(ArrayList< IDynamics > agents){ IVec separationForce = new IVec(); //zero vector int count = 0; for(int i=0; i < agents.size(); i++){ if(agents.get(i) instanceof MyBoid){ MyBoid b = (MyBoid)agents.get(i); if(b != this){ double dist = b.pos().dist(pos()); if(dist < separationDist && dist!=0 ){ IVec force = pos().dif(b.pos()); force.len(separationDist - dist); //the closer the larger separationForce.add(force); count++; } } } } if(count > 0){ separationForce.div(count); //average force separationForce.mul(separationRatio); push(separationForce); } } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }
Other agents within the range of the threshold distance of alignment (alignmentDist) are counted as neighbors and the average of their velocities is calculated. Then the difference between the average velocity and the current agent's are measured and the force is added to the direction of the vector difference of these two velocities. The amount of force is adjusted by the coefficient alignmentRatio.
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(100); for(int i=0; i < 100; i++){ new MyBoid(IRand.pt(100,100,0), IRand.pt(-5,-5,0,20,20,0)).clr(IRand.clr()); } } class MyBoid extends IParticle{ double alignmentDist = 40; double alignmentRatio = 5; IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void interact(ArrayList< IDynamics > agents){ IVec averageVelocity = new IVec(); //zero vector int count = 0; for(int i=0; i < agents.size(); i++){ if(agents.get(i) instanceof MyBoid){ MyBoid b = (MyBoid)agents.get(i); if(b != this){ if(b.pos().dist(pos()) < alignmentDist){ averageVelocity.add(b.vel()); count++; } } } } if(count > 0){ averageVelocity.div(count); IVec force = averageVelocity.sub(vel()); //difference of velocity push(force.mul(alignmentRatio)); } } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(150); for(int i=0; i < 100; i++){ new MyBoid(IRand.pt(100,100,0), IRand.pt(-5,-5,0,20,20,0)).clr(IRand.clr()); } } class MyBoid extends IParticle{ double cohesionDist = 50; double cohesionRatio = 5; double separationDist = 30; double separationRatio = 5; double alignmentDist = 40; double alignmentRatio = 5; IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void cohere(ArrayList< IDynamics > agents){ IVec center = new IVec(); //zero vector int count = 0; for(int i=0; i < agents.size(); i++){ if(agents.get(i) instanceof MyBoid && agents.get(i)!=this){ MyBoid b = (MyBoid)agents.get(i); if(b.pos().dist(pos()) < cohesionDist){ center.add(b.pos()); count++; } } } if(count > 0){ push(center.div(count).sub(pos()).mul(cohesionRatio)); } } void separate(ArrayList< IDynamics > agents){ IVec separationForce = new IVec(); //zero vector int count = 0; for(int i=0; i < agents.size(); i++){ if(agents.get(i) instanceof MyBoid && agents.get(i)!=this){ MyBoid b = (MyBoid)agents.get(i); double dist = b.pos().dist(pos()); if(dist < separationDist && dist!=0 ){ separationForce.add(pos().dif(b.pos()).len(separationDist - dist)); count++; } } } if(count > 0){ push(separationForce.mul(separationRatio/count)); } } void align(ArrayList< IDynamics > agents){ IVec averageVelocity = new IVec(); //zero vector int count = 0; for(int i=0; i < agents.size(); i++){ if(agents.get(i) instanceof MyBoid && agents.get(i) != this){ MyBoid b = (MyBoid)agents.get(i); if(b.pos().dist(pos()) < alignmentDist){ averageVelocity.add(b.vel()); count++; } } } if(count > 0){ push(averageVelocity.div(count).sub(vel()).mul(alignmentRatio)); } } void interact(ArrayList< IDynamics > agents){ cohere(agents); separate(agents); align(agents); } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(150); for(int i=0; i < 100; i++){ new MyBoid(IRand.pt(100,100,0), IRand.pt(-5,-5,0,20,20,0)).clr(IRand.clr()); } } class MyBoid extends IParticle{ double cohDist = 50; double cohRatio = 0.05; double sepDist = 30; double sepRatio = 0.05; double aliDist = 40; double aliRatio = 0.05; IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void interact(IDynamics agent){ if(agent instanceof MyBoid){ MyBoid b = (MyBoid)agent; if(dist(b) < cohDist) push( b.dif(this).mul(cohRatio) ); if(dist(b) < sepDist) push( dif(b).len((sepDist-dist(b))*sepRatio) ); if(dist(b) < aliDist) push( b.vel().dif(vel()).mul(aliRatio) ); } } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(150); for(int i=0; i < 100; i++){ MyBoid b = new MyBoid(IRand.pt(100,100,0), IRand.pt(-5,-5,0,20,20,0)); b.clr(IRand.clr()); b.cohesionDist(50); b.cohesionRatio(5); b.separationDist(30); b.separationRatio(5); b.alignmentDist(40); b.alignmentRatio(5); } } class MyBoid extends IBoid{ IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }
The first setting below have setting of smaller threshold distance for each rule. As result it creates smaller clusters of swarms.
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(100); for(int i=0; i < 100; i++){ MyBoid b = new MyBoid(IRand.pt(100,100,0), IRand.pt(-5,-5,0,20,20,0)); b.clr(IRand.clr()); b.cohesionDist(21); b.cohesionRatio(5); b.separationDist(10); b.separationRatio(5); b.alignmentDist(15); b.alignmentRatio(5); } } class MyBoid extends IBoid{ IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }
The next setting below has a smaller force ratio in the alignment rule. Because the force to align the velocities of agents are weak, it oscillates around the center with the attraction and repulsion forces from the cohesion and separation rule, not converging into the same velocity for a while.
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(300); for(int i=0; i < 100; i++){ MyBoid b = new MyBoid(IRand.pt(100,100,0), IRand.pt(-5,-5,0,20,20,0)); b.clr(IRand.clr()); b.cohesionDist(50); b.cohesionRatio(5); b.separationDist(30); b.separationRatio(5); b.alignmentDist(40); b.alignmentRatio(0.5); } } class MyBoid extends IBoid{ IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }
The setting below has a larger threshold distance for the separation rule and a smaller threshold distance for the cohesion rule. Because of this inverted order of cohesion and separation, agents try to go away but still creating smaller clusters with the alignment rule.
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(800); for(int i=0; i < 100; i++){ MyBoid b = new MyBoid(IRand.pt(100,100,0), IRand.pt(-5,-5,0,20,20,0)); b.clr(IRand.clr()); b.cohesionDist(30); b.cohesionRatio(5); b.separationDist(50); b.separationRatio(5); b.alignmentDist(40); b.alignmentRatio(5); } } class MyBoid extends IBoid{ IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }