Thursday, September 25, 2008

Boids Motion

Warning the post contains programming!

Ok, so the other day I stumbled across this website and decided to implement a basic boid flock in Flash. For those who don't know, Boid Flocking is a mathmatical way (ie. no random) way of moving a number (more than 3) objects that represents a flock. In this example, they move in 2D space, but it could easily to be expanded to 3D (or indeed simplified to 1D...). This theory was developed by Craig Reynolds. After creating a basic model, I created a simple game which I submitted to Newgrounds. From a game designers point of view, and clearly the reviews point of view, it is horrible, however, I built it mainly for demonstration purposes of boid stuff.

Boids run on 3 main rules. The first, and most important rule, is that they head towards the average mass of the boids.

_root.toalPosX = 0;
_root.toalPosY = 0;
for(i = 1; i<11;>
{
var numx:Number = _root["boid"+i+"_mc"]._x;
toalPosX = toalPosX + numx;
var numy:Number = _root["boid"+i+"_mc"]._y;
toalPosY = toalPosY + numy;
}
var averagePosX:Number = _root.toalPosX/10;
_root.averagePosY = _root.toalPosY/10;

This only works with 11 Boids, and I would like to, at some point, change to that to a foreach statement, however, as I have mainly been using C# recently, and I wasn't sure how you did it in Flash. So, as you can see, it defines two variables, one for X and one for Y (That is another thing you will miss going in to Flash from C#, things like Vector2s) Then, by cycling through each boid, and adding their x and y's to their respective variables, I was able to deduce the average location. This is placed within onEnterFrame, so the center of mass is always moving.

The thing that I think I did wrong was with how their velocities are handeled. As you can see from the NG movie, they tend to shoot off and the second their mass moves in one direction, it keeps moving, much like if you are swinging a bucket of water, the second the mass goes past the mid point you will suddenly lurch in that direction.

However, I actually added a 4th rule (I will come back to the other 2) that means the mass moves towards the red dot in the game (normally I would have it roughtly tracking the mouse) However, it keeps shooting past at increasing velocity.

for(i = 1; i<11;>
{
if(_root["boid"+i+"_mc"]._x <>
{
_root["bVolX"+i]+=1;
}
else if(_root["boid"+i+"_mc"]._x >averagePosX)
{
_root["bVolX"+i]-=1;
}

if(_root["boid"+i+"_mc"]._y <>
{
_root["bVolY"+i]+=1;
}
else if(_root["boid"+i+"_mc"]._y >averagePosY)
{
_root["bVolY"+i]-=1;
}

}

As you can see the numbers just keep getting bigger. I am trying to think of a way to stop them growing so huge, I am sure there must be something I missed in the implementation, because they are meant to basically stay within the bounds of the screen.

Anyway, back to my implentation. The next rule, which deffenitley has a noticable effect is a little bit of repulsion. I used twin for loops to check boid i against boid j where i != j. When I first did this, for reasons best known only to me, I put the signs as the same, so although they were repelling, they were both going the same way, so they just got a little bit faster. Better now.

for(i = 1; i <11; j =" 1;" distancex =" _root[" distancey =" _root["> -150)
{
_root["bVolX"+i] -= 1;
_root["bVolX"+j] += 1;
}
else if (distanceX > 0 && distanceX <> -150)
{
_root["bVolY"+i] -= 1;
_root["bVolY"+j] += 1;
}
else if (distanceY > 0 && distanceY <>

Fairly self explanatory. The effect looks a little bit like opposite magnets. One thing to note is this does not prohibit collisions, if they are travelling fast enough this will merely slow them down. What I would be interested in seeing done is where the two boids involved in the collision spin off in a random direction. To do this, I would hitTest within these for loops. What might make more sense if if you times the bVolX by -0.8 to flip it, and bVolY to add a bit of variation so it doesn't go straight back, by -1.2. Then the other boid would be times by these two values switched for X and Y. Maybe I will try this, points to anyone who beats me.

The final rule, and probably the least important, is a speed checker. Boids will try to mimick other Boids average speed. If most boids are going faster, it will speed up and vice versa. The idea behind this is they should meet somewhere in the middle and not go to fast. Well, thats the idea anyway.

I did this simply by checking the boids velocity against the average velocity and adding or subtracting depending on this.

avgVelX =0 ;
totVelX = 0
for(i = 1; i<11;>
{
totVelX += _root["bVolX"+i]
}
avgVelX = totVelX/10;
for(i = 1; i<11;>
{
if(avgVelX >= 0)
{
if(_root["bVolX"+i] <>
{
bVolX += 2;
}
else if (_root["bVolX"+i] > avgVelX)
{
bVolX -= 2;
}
} else
{
if(_root["bVolX"+i] <>
{
bVolX -= 2;
}
else if (_root["bVolX"+i] > avgVelX)
{
bVolX += 2;
}
}
}

avgVelY =0 ;
totVelY = 0
for(i = 1; i<11;>
{
totVelY += _root["bVolY"+i]
}
avgVelY = totVelY/10;
for(i = 1; i<11;>
{
if(avgVelY >= 0)
{
if(_root["bVolY"+i] <>
{
bVolX += 2;
}
else if (_root["bVolY"+i] > avgVelY)
{
bVolY -= 2;
}
} else
{
if(_root["bVolY"+i] <>
{
bVolY -= 2;
}
else if (_root["bVolY"+i] > avgVelY)
{
bVolY += 2;
}
}
}

The only thing missing is the red dot you control in the game. I was trying to make something that followed the mouse, except just a bit slower, but ended up with a Boid you controlled. It works exactly the same as all the other boids, except the centre of mass is always the mouse and it does not copy speed, nor is it repelled by other boids.

I hope this has helped anyone trying to implement a Boid thing. It is a really nice programming challenge, trying to interpret the rules in to your programming lanaguage. The website I listed at the top was a great resource. Any questions, please leave a comment or email me @ thekileyenator@gmail.com

Thanks for reading!

No comments: