Builtin Library
README.md
boids
Boids flocking simulation — separation, alignment, and cohesion with obstacle avoidance (physics raycasts), soft sphere/box boundary containment, predator flee, and leader follow. Hot loop uses structure-of-arrays storage and a spatial-grid neighbour query. Sub-modules under this folder hold the simulation primitives:
boids.core— spatial grid, struct-of-arrays storage, math.boids.steering— per-behaviour force functions.boids.spawning— sphere / grid spawn patterns.
Exports
M.createFlock(opts: FlockOpts?) -> Flock— spawn a new flock and return its handle.
Flock methods (called as flock:method(...)):
flock:update(dt: number)— advance the simulation one tick.flock:addPredator(entityId: string, opts: PredatorOpts?)flock:removePredator(entityId: string)flock:setLeader(entityId: string, opts: LeaderOpts?)flock:clearLeader()flock:setWeights(weights: WeightUpdate)— partial update.flock:setBoundary(boundary: Boundary?)flock:addBoids(count: number, origin: Vec3?)— spawn extras at runtime.flock:getCount() -> numberflock:getCenterOfMass() -> CenterOfMassflock:destroy()— despawn every boid entity.
Types:
Vec3 = { x: number, y: number, z: number, [number]: number? }Boundary = SphereBoundary | BoxBoundary(seeboids.steering).FlockOpts— all configuration keys are optional; see the type definition ininit.luaufor the full list.PredatorOpts = { fleeDistance?: number, fleeWeight?: number }LeaderOpts = { followDistance?: number, followWeight?: number }WeightUpdate— partial overrides applied bysetWeights.CenterOfMass = { x: number, y: number, z: number }
Usage
local Boids = require("@builtin::modules.boids")
local flock = Boids.createFlock({
count = 100,
model = "cube",
spawnOrigin = { x = 0, y = 5, z = 0 },
spawnRadius = 10,
boundary = { type = "sphere", center = { x = 0, y = 0, z = 0 }, radius = 50 },
})
-- In your component's update:
function update(dt)
flock:update(dt)
end
Notes
- Each call to
flock:update(dt)writes Transform position + rotation for every alive boid via__reflect.set. There is no batching — the number of native calls scales linearly with the flock size. - The neighbour buffer (
_neighborBuf) is reused between ticks; do not reach in and modify it externally. flock:destroydespawns boid entities but does not free the Flock table itself. Drop the reference once destroyed.- Spatial grid cell size is the same as
neighborRadius. Changing the radius at runtime viasetWeightsis not currently rewired into the grid; for now, create a new flock instead.