Hello guys,
Boids is a well known artificial life algorithm, developed by Craig Reynolds in 1986. It simulates the behaviour of swarms by three simple rules:
- separation: steer to avoid crowding local flockmates
- alignment: steer towards the average heading of local flockmates
- cohesion: steer to move toward the average position (center of mass) of local flockmates
I made no pictures for the boids, just colored circles. So it looks a bit like flying confetti or bouncy balls. There are several possibilities to improve it farther and I know there are much better implementations out there, but once I got this working I have not much interest in improving it anymore. I stumbled over it reading AI Game Programming Wisdom and wanted to try it out since then. A challenge on poisonhack made me doing so.
Have a look here for a preview:
http://tinypic.com/r/246kwwl/7This sample is with a low flock radius.
The program has some settings at startup. You can experiment a bit with them.
number of boids: should be clear
velocity limit: maximum speed
radius limit: the radius in which another boid is declared as too near, so the actual boid has to move away from it.
speed reduction: boids would be too fast without it. The higher the number, the lower the speed.
flock radius: the radius in which other boids are seen as of the same flock
Not every possible setting is reasonable. Have fun and maybe make a better one.
LOC: 504 only source
640 with comments and empty lines
Main algorithm:
package model;
import java.awt.Color;
import java.awt.Graphics;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
public class Boid {
private static int boidNr;
private final int panelWidth;
private final int panelHeight;
private Vector position;
private Vector positionToDraw;
private Vector velocity = new Vector(0, 0);
private Color color;
private final static List<Boid> boids = new LinkedList<Boid>();
private static int updateSkip;
private static int updates = 1;
private static int velocityLimit;
private static int radiusLimit;
private static int flockRadius;
public Boid(int pwidth, int pheight) {
this.panelWidth = pwidth;
this.panelHeight = pheight;
setRandomPosition();
setRandomColor();
}
public Boid(Boid boid) {
this.panelWidth = boid.panelWidth;
this.panelHeight = boid.panelHeight;
this.position = new Vector(boid.position);
this.positionToDraw = new Vector(boid.positionToDraw);
this.velocity = new Vector(boid.velocity);
this.color = boid.color;
}
public static void initBoids(int pwidth, int pheight) {
for (int i = 0; i < boidNr; i++) {
boids.add(new Boid(pwidth, pheight));
}
}
public static void drawBoids(Graphics g) {
for (Boid boid : boids) {
boid.draw(g);
}
}
public static void updateBoids() {
updates++;
if (updates == updateSkip) {
updates = 1;
for (Boid boid : boids) {
boid.updateWithBounds();
}
}
for (Boid boid : boids) {
// makes the boids move slowlier
boid.positionToDraw = new Vector(boid.position);
boid.positionToDraw.subtract(boid.velocity);
Vector ivel = new Vector(boid.velocity);
ivel.multiply(updates / (double) updateSkip);
boid.positionToDraw.add(ivel);
}
}
private void setRandomPosition() {
Random rand = new Random();
position = new Vector();
position.x = rand.nextInt(panelWidth);
position.y = rand.nextInt(panelHeight);
}
private void draw(Graphics g) {
g.setColor(color);
g.fillOval((int) positionToDraw.x, (int) positionToDraw.y, 10, 10);
}
private void setRandomColor() {
Random rand = new Random();
this.color = new Color(rand.nextInt(256), rand.nextInt(256),
rand.nextInt(256));
}
private void updateWithoutBounds() {
velocity.add(rule1(), rule2(), rule3());
limitVelocity();
position.add(velocity);
if (position.x > panelWidth) {
position.x = 0;
}
if (position.x < 0) {
position.x = panelWidth;
}
if (position.y > panelHeight) {
position.y = 0;
}
if (position.y < 0) {
position.y = panelHeight;
}
}
private void updateWithBounds() {
velocity.add(rule1(), rule2(), rule3());
limitVelocity();
position.add(velocity);
if (position.x > panelWidth) {
position.x = panelWidth;
velocity.x = -velocity.x;
}
if (position.x < 0) {
position.x = 0;
velocity.x = -velocity.x;
}
if (position.y > panelHeight) {
position.y = panelHeight;
velocity.y = -velocity.y;
}
if (position.y < 0) {
position.y = 0;
velocity.y = -velocity.y;
}
}
/**
* boids try to fly towards the centre of mass of neighbouring boids
*
* @return resulting vector
*/
private Vector rule1() {
Vector center = new Vector(0, 0);
List<Boid> flock = flock();
for (Boid boid : flock) {
if (!boid.equals(this)) {
center.add(boid.position);
}
}
center.divide(flock.size() - 1);
Vector result = new Vector(center);
result.subtract(this.position);
result.divide(100);
return result;
}
/**
* try to keep a small distance away from other objects
*
* @return
*/
private Vector rule2() {
Vector result = new Vector(0, 0);
for (Boid boid : flock()) {
if (!boid.equals(this)) {
if (isClose(boid.position)) {
Vector sub = new Vector(boid.position);
sub.subtract(position);
result.subtract(sub);
}
}
}
return result;
}
/**
* boids try to match velocity with near boids
*
* @return
*/
private Vector rule3() {
Vector pvj = new Vector(0, 0);
List<Boid> flock = flock();
for (Boid boid : flock) {
if (!boid.equals(this)) {
pvj.add(boid.velocity);
}
}
pvj.divide(flock.size() - 1);
pvj.subtract(velocity);
pvj.divide(;
return pvj;
}
private void limitVelocity() {
if (velocity.length() > velocityLimit) {
velocity.divide(velocity.length());
velocity.multiply(velocityLimit);
}
}
public List<Boid> flock() {
List<Boid> flock = new LinkedList<Boid>();
for (Boid boid : boids) {
if (isInFlock(boid.position)) {
flock.add(boid);
}
}
return flock;
}
private boolean isInFlock(final Vector pos) {
Vector range = new Vector(position);
range.subtract(pos);
return range.length() < flockRadius;
}
private boolean isClose(final Vector pos) {
Vector range = new Vector(position);
range.subtract(pos);
return range.length() < radiusLimit;
}
public static void setOptions(Options options) {
velocityLimit = options.velocityLimit;
radiusLimit = options.radiusLimit;
boidNr = options.boidNr;
updateSkip = options.speedReduction;
flockRadius = options.flockRadius;
}
}
Deque