Er, I don’t have anything that meets your specific request, but here’s a fun little algorithm for simulating schooling in an aquarium game that I wrote. There’s an absurd amount of comments throughout it because I have shared it with people before and wanted to explain my thought process. Fluther’s gonna kill the tabbing though so good luck reading it.
/*
Schooling algorithm. Establish a “leader” fish (the first fish of this species occurring in the tank.fish list).
The leader fish swims normally, but all other fish of its species will be drawn towards it. This is achieved by
adding a vector towards the leader fish’s position to other fish’s velocities.
Effect: No effect if this fish is the leader fish. Otherwise, make a change to this fish’s velocity.
*/
public void pullTowardsSchool(){
//iterate to find the leader fish
for(int i = 0; i < tank.fish.size(); i++){
Fish f = (Fish) tank.fish.get(i);
if(f.name == this.name){
//if leader fish is this fish (determined by fish’s nicknames – nicknames are unique,
//so name comparison is a safe and efficient way to determine equality), no effect.
return;
}
else if(f.species == this.species){
Vector3D pullToward = f.position;
//BUT, we don’t want to move toward the leader fish’s exact position.
//instead we pull toward a point that’s near him, offset by some pseudorandom amount
//we base this offset on the fish’s name so that it is consistent
//so one fish will consistently tend to be above the leader, another behind, etc
//this helps prevent the fish from just piling up on top the leader
int offset = int(name.toCharArray())[0];
pullToward = pullToward.addVector(new Vector3D(offset, offset, offset));
//convert to a vector pointing from the current position to the desired position and normalize
pullToward = pullToward.addVector(this.position.multiplyScalar(-1)).normalize();
//schoolingCoefficient is the “strength” of the pull, different for each species
//(since some real fish species school more tightly than others)
this.velocity = this.velocity.addVector(pullToward.multiplyScalar(this.schoolingCoefficient));
return;
}
}
}