Boids
November 12, 2007

I recently came across an article that mentioned boids, which is essentially a computer simulation of a flock a birds. Boids uses three simple rules, from which complex behaviour emerges. Having studied large scale distributed systems I’m familiar with emergent behaviour, but I haven’t seen anything that looks as impressive as boids.
The three rules of the Boid system are:
Rule 1: Don’t overcrowd near-by boids
Rule 2: Move closer to near-by boids
Rule 3: Move in the general direction of the other boids
I thought I’d have a crack at implementing my own boids. I hadn’t used PyGame before, but it looked ideal for this project.
First I came up with a basic boid class that stores the boids position and velocity.
class Boid:
def __init__(self, x, y):
self.x = x
self.y = y
self.velocityX = random.randint(1, 10) / 10.0
self.velocityY = random.randint(1, 10) / 10.0
I added a method to move the boid based on its velocity, and a method to detect the distance between two boids. Then I added the code for each of the rules.
Rule 1: Don’t overcrowd near-by boids.
"Move away from a set of boids. This avoids crowding"
def moveAway(self, boids, minDistance):
if len(boids) < 1: return
distanceX = 0
distanceY = 0
numClose = 0
for boid in boids:
distance = self.distance(boid)
if distance < minDistance:
numClose += 1
xdiff = (self.x - boid.x)
ydiff = (self.y - boid.y)
if xdiff >= 0: xdiff = math.sqrt(minDistance) - xdiff
elif xdiff < 0: xdiff = -math.sqrt(minDistance) - xdiff
if ydiff >= 0: ydiff = math.sqrt(minDistance) - ydiff
elif ydiff < 0: ydiff = -math.sqrt(minDistance) - ydiff
distanceX += xdiff
distanceY += ydiff
if numClose == 0:
return
self.velocityX -= distanceX / 5
self.velocityY -= distanceY / 5
Rule 2: Move closer to near-by boids.
"Move closer to a set of boids"
def moveCloser(self, boids):
if len(boids) < 1: return
# calculate the average distances from the other boids
avgX = 0
avgY = 0
for boid in boids:
if boid.x == self.x and boid.y == self.y:
continue
avgX += (self.x - boid.x)
avgY += (self.y - boid.y)
avgX /= len(boids)
avgY /= len(boids)
# set our velocity towards the others
distance = math.sqrt((avgX * avgX) + (avgY * avgY)) * -1.0
self.velocityX -= (avgX / 100)
self.velocityY -= (avgY / 100)
Rule 3: Move in the general direction of the other boids
"Move with a set of boids"
def moveWith(self, boids):
if len(boids) < 1: return
# calculate the average velocities of the other boids
avgX = 0
avgY = 0
for boid in boids:
avgX += boid.velocityX
avgY += boid.velocityY
avgX /= len(boids)
avgY /= len(boids)
# set our velocity towards the others
self.velocityX += (avgX / 40)
self.velocityY += (avgY / 40)
So that was the boid code sorted. Then all that was required was some code to tie it all together and display the boids:
pygame.init()
size = width, height = 800, 600
black = 0, 0, 0
maxVelocity = 10
numBoids = 50
boids = []
screen = pygame.display.set_mode(size)
ball = pygame.image.load("ball.png")
ballrect = ball.get_rect()
# create boids at random positions
for i in range(numBoids):
boids.append(Boid(random.randint(0, width), random.randint(0, height)))
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
for boid in boids:
closeBoids = []
for otherBoid in boids:
if otherBoid == boid: continue
distance = boid.distance(otherBoid)
if distance < 200:
closeBoids.append(otherBoid)
boid.moveCloser(closeBoids)
boid.moveWith(closeBoids)
boid.moveAway(closeBoids, 20)
# ensure they stay within the screen space
# if we roubound we can lose some of our velocity
border = 25
if boid.x < border and boid.velocityX < 0:
boid.velocityX = -boid.velocityX * random.random()
if boid.x > width - border and boid.velocityX > 0:
boid.velocityX = -boid.velocityX * random.random()
if boid.y < border and boid.velocityY < 0:
boid.velocityY = -boid.velocityY * random.random()
if boid.y > height - border and boid.velocityY > 0:
boid.velocityY = -boid.velocityY * random.random()
boid.move()
screen.fill(black)
for boid in boids:
boidRect = pygame.Rect(ballrect)
boidRect.x = boid.x
boidRect.y = boid.y
screen.blit(ball, boidRect)
pygame.display.flip()
I’m really impressed with the results! From the interaction of three simple rules some quite complex behaviour emerges.
Hi there!
Is it possible to obtain a copy of your finished script? I’ve been interested in things like flocking behaviours for a while, but my knowledge of mechanics and the like isn’t good enough for me to get something working properly – I’d be interested to see how you did it. (I tried piecing together your code and filling in the missing functions, but I clearly didn’t do it right
)
Thanks!
Comment by James Frost — May 18, 2008 @ 2:00 pm
All the code is listed here. You will need to import pygame, but once you’ve done that there shouldn’t be any missing functions.
What errors have you seen when you try to run it?
Comment by admin — May 18, 2008 @ 5:16 pm
The functions to move the boids, and to get their distance to another boid are missing. I tried to re-create them, but clearly something’s rather wrong with my maths, as all the boids just head to the 4 corners of the screen!
Comment by James Frost — May 18, 2008 @ 5:19 pm
Sorry, I thought I’d posted all of the code, but you’re right some is missing. I’ve added a link at the bottom to download the full source code.
Let me know if you’ve got any questions about any of it.
Comment by admin — May 18, 2008 @ 6:44 pm
That’s fantastic – thank you!
Comment by James Frost — May 18, 2008 @ 6:46 pm
My guess on the missing functions:
at the beginning add:
import pygame
import random
import math
in the Boid class add:
def distance(self, bb):
return math.sqrt((self.x-bb.x)**2 + (self.y -bb.y)**2)
def move(self):
self.x += self.velocityX
self.y += self.velocityY
Comment by lgwe — March 5, 2009 @ 12:09 pm
@lgwe: Yes. Sorry, the download link to the full source code (including this missing code) was broken. I’ve just fixed it.
Comment by Ben — March 5, 2009 @ 12:32 pm
Looks good but i think it is only 2d correct? Is there a 3d version? i use blender so i guess i would have to change it a bit, the boids at blender work great but are not supported by the game engine ( i am trying to develop a sim) so i am trying to write my own boids. I was trying to make 3d boids that would also scatter when a predator comes around. I just stared learning python but don’t know much, any help you can give or if you have a 3d version would be great
Comment by Rocco — October 28, 2009 @ 3:27 pm
Hi Rocco,
Yes this is a 2D version of boids. A 3D version would follow exactly the same principles, you just need to add extra calculations for the z axis. So where the x and y values are calculated just as another calculation for z.
Hope that helps!
Ben
Comment by Ben — October 28, 2009 @ 3:44 pm
Yaeh, that’s what i thought. One last question: there are several references to pygame which of course i will get rid of but that ruins the code. i am taking acloser look to see what fixes i can make, in the meantime any help you can offer. I am using thescript in Blender game engine, which is easy to use. of i added what i needed to make it work in blender but am a little hazy on how to fill the pygame while loop for example
Comment by Rocco — October 29, 2009 @ 3:54 pm
Hi Rocco,
I don’t know much about the Blender game engine, but I imagine it has something similar to the pygame main game loop. Sorry I couldn’t be of any more help!
Ben
Comment by Ben — November 3, 2009 @ 8:03 pm