Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

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() -> number
  • flock:getCenterOfMass() -> CenterOfMass
  • flock:destroy() — despawn every boid entity.

Types:

  • Vec3 = { x: number, y: number, z: number, [number]: number? }
  • Boundary = SphereBoundary | BoxBoundary (see boids.steering).
  • FlockOpts — all configuration keys are optional; see the type definition in init.luau for the full list.
  • PredatorOpts = { fleeDistance?: number, fleeWeight?: number }
  • LeaderOpts = { followDistance?: number, followWeight?: number }
  • WeightUpdate — partial overrides applied by setWeights.
  • 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:destroy despawns 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 via setWeights is not currently rewired into the grid; for now, create a new flock instead.