Boid Simulation

Hello everybody!

Recently I have got invested in boids. I have made 90% of the system, but I feel that it is messy/unoptimized and I have yet to figure out a way to apply the third principle, separation. The code is well documented so you should hopefully be able to figure it out.

TL; DR. Need help finishing my code and cleaning up bad practices!
Wikipedia: Boids - Wikipedia

local boidClass = {}
boidClass.__index = boidClass

local config = require(script.Config)
local collectionService = game:GetService("CollectionService")

function boidClass:_construct()
	-- create a boid with a physical obj
	local boid = setmetatable({}, boidClass)
	boid.object = boidClass._setup()
	return boid

function boidClass._setup()
	local new_boid = script.Boid:Clone() -- grab boid object and clone it
	new_boid.Parent = game.Workspace
	new_boid.Position =, 10), 10, math.random(-10, 10))
	new_boid.CFrame = CFrame.lookAt(new_boid.Position,, 5), 10, math.random(-5, 5)))
	collectionService:AddTag(new_boid, "Boid") -- tag object as Boid
	return new_boid

function boidClass:_localBoids()
	-- return boids within a 6-stud radius
	local function filterForBoids(filter_table)
		local boidTable = {}
		for _, object in ipairs(filter_table) do
			if collectionService:HasTag(object, "Boid") then -- check if object is a boid
				table.insert(boidTable, object)
		return boidTable -- return the table, now boids only
	local fieldOfVision =, 12, 12) -- radius is half of diameter, 6-studs in each direction
	local touching = game.Workspace:GetPartBoundsInBox(self.object.CFrame, fieldOfVision)
	return filterForBoids(touching) -- return table of boids, or nil

function boidClass:_centerOfFlock(boid_table)
	local avgPosition =, 0, 0)
	for _, boid in ipairs(boid_table) do
		avgPosition += boid.Position
	return avgPosition / #boid_table

function boidClass:_alignedCenter(center_position, boid_table)
	local avgAlignment =, 0, 0)

	for _, boid in ipairs(boid_table) do
		avgAlignment += boid.CFrame.lookVector * config.boid.speed

	return ((avgAlignment / #boid_table) + (center_position)) / 2

function boidClass:_update()
	-- update boid position/cframe
	local localBoids = self:_localBoids() -- boids within the local space
	local centerOfFlock = self:_centerOfFlock(localBoids) -- average position of local boids
	local alignedCenter = self:_alignedCenter(centerOfFlock, localBoids) -- average between flock direction and center position
	self.object.CFrame = CFrame.lookAt(self.object.Position, centerOfFlock) -- point the boid towards midway between next projected position and flock center
	self.object.Position += self.object.CFrame.lookVector * config.boid.speed -- move boid forward by config speed
	self.object.Position =, -25, 25), math.clamp(self.object.Position.Y, 25, 25), math.clamp(self.object.Position.Z, -50, 50)) -- clamp boid position within bounds
	-- line 64 = cohesion + alignment

return boidClass

Feel free to submit pull requests and I will review them. Any help with cleaning up my code or adding the third principle for boids! Thanks for reading.

I believe that Code Review expects some code to be presented inside the Dev Forum.
Could you give us a copy here?

1 Like

I agree with @RamJoT. A copy of the script contents on the DevForum would be great.

My feedback:

  1. You cannot destroy the classes, but you should probably be able to
  2. :_construct is kind of a rare way to instantiate a class. The most common way is to define a .new method.
  3. Maybe use Roblox’ code style
  4. Avoid magic numbers if you can

self.object.Position =, -25, 25), math.clamp(self.object.Position.Y, 25, 25), math.clamp(self.object.Position.Z, -50, 50))

  1. Use instead of, 0, 0)

Also, if you enjoy working with Git, you can use Rojo.

1 Like

I have included the BoidClass script on the post.

  1. If I cannot destroy them, then how would I do it?
  2. Alrighty! Is it going to impact the script or is it simply an unusual way?
  3. I do, but I was testing out a new style.
  4. What do you mean by magic numbers?
  5. Thank you! I did not know that existed!

Thank you for the feedback. Hopefully you can answer my questions soon!

Magic numbers are floating constants throughout your code that are seemingly without context. You can fix this by storing them in variables and suggesting what they do by their names. This is not a big deal, but it might make your code harder to understand.

It is optional, but I personally like to be able to call :Destroy() on my instance classes. You will probably not have a use case for this at the moment.

Another great resource that you might like is Wally. It is a package manager and it allows you to import some high quality scripts with versioning. You can use it in a Rojo project.

This style guide is quite popular and even maintained via Roblox’s GitHub account.

1 Like

That seems to answer all of my questions. I know how to write code in Roblox’s style, I was just trying out something new.