This example explains how to implement Craig Reynold's Boids [1] in JBotSim. The purpose is to simulate bird flocks. There is no global behavior implemented because the flock behavior emerge from three rules. We use the boids pseudocode written by Conrad Parker [2]. The position of a boid in round i is determined by the position and speed of its neighbors in round i-1.
You can download an executable JAR that
contains the source code and a copy of JBotSim.
(Run by typing java -jar boids.jar.)
In JBotSim, we redefine the following methods:
onPreClock, onClock and onPostClock. We
have the guarantee that onPreClock will first executes on all
the boids before onClock is executed, and same between
onClock and onPostClock. Hence, all the boids first
find their neighbors, then they compute the next location, then apply
the resulting movement.
@Override // Look around
public void onPreClock(){
theNeighbors.clear();
List<Node> sensedNode = getSensedNodes();
for (Node aNode : sensedNode){
if (aNode instanceof Boid)
this.theNeighbors.add((Boid) aNode);
}
}
@Override // Compute movement
public void onClock(){
nextVelocity = velocity;
nextVelocity.add(groupFlock());
nextVelocity.add(collisionAvoidance());
nextVelocity.add(matchVelocity());
nextVelocity.add(new Vector2D(0.02,0.0)); // Wind
nextVelocityMax(); // Animal limitation
}
@Override // Move
public void onPostClock(){
Point target = new Point();
velocity = nextVelocity;
target.setLocation(getLocation().getX()+velocity.getX(),
getLocation().getY()+velocity.getY());
setDirection(target);
move(velocity.distance());
wrapLocation();
}
Each boid has three rules:
We also add a method to limit the speed of the boids.
public Vector2D groupFlock(){
Vector2D center = new Vector2D();
for (Boid aBoid : theNeighbors) {
if(!aBoid.equals(this)) {
center.add(aBoid.getLocation().getX(),aBoid.getLocation().getY());
}
}
if (theNeighbors.size()>0){
center.division(theNeighbors.size());
center.substract(getLocation().getX(),getLocation().getY());
center.division(1000.);
}
return center;
}
public Vector2D collisionAvoidance(){
Vector2D avoid = new Vector2D();
for (Boid aBoid : theNeighbors) {
if(!aBoid.equals(this)) {
if(distance(aBoid)<60){
Vector2D tempDistance = new Vector2D(
aBoid.getLocation().getX()-getLocation().getX(),
aBoid.getLocation().getY()-getLocation().getY());
avoid.substract(tempDistance);
}
}
}
avoid.division(20.);
return avoid;
}
public Vector2D matchVelocity(){
Vector2D global = new Vector2D();
for (Boid aBoid : theNeighbors) {
if(!aBoid.equals(this)) {
global.add(aBoid.getVelocity());
}
}
if (theNeighbors.size()>0){
global.division(theNeighbors.size());
global.substract(velocity);
global.division(8.);
}
return global;
}
[1] Reynolds' page, http://www.red3d.com/cwr/boids/
[2] Parker's page, http://www.kfish.org/boids/pseudocode.html