Boids Simulation: Part 2
In part 1 of our boids series, I showed how to get the bare minimum up and running — a window that draws some stationary boids. In this second part I will show how to make the boids move.
Boids should choose their movement by reacting to the other boids around them, and following the flocking rules. We are building our simulation in stages, though — so we’ll start by just getting the boids moving with a fixed velocity, away from the centre located at (0.5, 0.5):
boid :: Chanout (Float, Float) -> (Float, Float) -> CHP () boid out initPos@(initPosX, initPosY) = boid' initPos where fixedVelX = 0.01 * (initPosX - 0.5) fixedVelY = 0.01 * (initPosY - 0.5) boid' pos@(posX, posY) = do writeChannel out pos boid' (posX + fixedVelX, posY + fixedVelY)
darcs get --tag p2a http://patch-tag.com/r/twistedsquare/chp-boids chp-boids-p2a
We don’t need to touch any of the rest of the code to change the boid’s behaviour. The boid’ function alters its position every time it recurses, by a fixed amount. If you run this program, you will notice a strange effect — or rather a lack of effect. Nothing is moving! The reason for this is that our boid only moves each time it sends its position. It only sends its position when the other end asks for it. The other end is the OpenGL display callback, which only asks for the position when it redraws the screen, and the screen is only redrawn when necessary. Hence, no movement. (If you drag the window so that part of it disappears off the side of the screen and thus needs a redraw, you will get movement.)
Our architecture is actually that of information pull. The display function pulls the position from the boid, and the boid only updates its position once this has happened. We have a form of concurrent laziness in our program. There are several ways we could fix our program to get the regular movement that we intended. For now, we will add an OpenGL timer that redisplays every 50ms, giving us 20 updates per second. (This is not perfect either, as triggering additional redraws will speed up the boids, but we will return to this issue later in the tutorial.) I’ll not bother with the code here (it’s purely GLUT code), but you can see it by issuing:
darcs get --tag p2b http://patch-tag.com/r/twistedsquare/chp-boids chp-boids-p2b
Now if you compile and run our demonstration, you should see the boids gradually moving out from the centre. (In fact, since the boids all move away from the centre, it looks like a classic starfield effect.) The next problem is that the boids fly off the sides of the window; we can make the window wrap round by clamping the boids’ positions to the range (0,1). The code is quite small, but if you run the new version, you can see that with the latest versions we now have boids moving across the screen and wrapping around:
darcs get --tag p2c http://patch-tag.com/r/twistedsquare/chp-boids chp-boids-p2c
Our boids are on the move! That will do for this update; next time we will start to add the infrastructure necessary for the boids to know where their neighbours are and act upon it.