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