Builtin Library
README.md
steering
Steering behaviours for the boids simulation — obstacle avoidance,
boundary containment, predator flee, and leader following. Each
function returns force components (fx, fy, fz) the caller accumulates
onto the boid's steering force before integration. Position and
velocity scalars are passed individually to avoid per-call vec3
allocation.
Exports
S.obstacleAvoidance(px, py, pz, vx, vy, vz, avoidDist, avoidWeight) -> (number, number, number)— raycast ahead along velocity; if it hits an obstacle, push along the hit normal with quadratic-falloff urgency. Safe no-op when__physics.raycastis unavailable.S.boundaryForce(px, py, pz, boundary: Boundary, force) -> (number, number, number)— soft containment force inside the outer 30% of a sphere or box boundary; zero inside.S.predatorFlee(px, py, pz, predators: { Predator }) -> (number, number, number)— accumulated flee force from every predator inside its individualfleeDistance. Predator positions are read from each predator entity'sTransform.S.leaderFollow(px, py, pz, leader: Leader) -> (number, number, number)— steer toward a leader entity, scaling with overshoot beyond the follow distance. Reads leader position from the leader entity'sTransform.
Types:
Vec3 = { x: number, y: number, z: number, [number]: number? }— map or array.SphereBoundary = { type: "sphere", center: Vec3, radius: number }.BoxBoundary = { type: "box", min: Vec3, max: Vec3 }.Boundary = SphereBoundary | BoxBoundary.Predator = { entityId: string, fleeDistance: number, fleeWeight: number }.Leader = { entityId: string, followDistance: number, followWeight: number }.
Usage
local Steering = require("@builtin::modules.boids.steering")
local ax, ay, az = Steering.obstacleAvoidance(px, py, pz, vx, vy, vz, 3.0, 5.0)
local bx, by, bz = Steering.boundaryForce(px, py, pz,
{ type = "sphere", center = { x = 0, y = 0, z = 0 }, radius = 50 }, 2.0)
local px2, py2, pz2 = Steering.predatorFlee(px, py, pz, predators)
local lx, ly, lz = Steering.leaderFollow(px, py, pz, leader)
Notes
- All four functions read entity / physics state at call time. Caller ordering inside the boids inner loop matters — these run after the spatial grid query and before integration.
obstacleAvoidancecalls__physics.raycastthroughpcall. A missing physics binding (e.g. in a non-3D test context) yields a silent no-op rather than a runtime error.boundaryForcereturns zero for unrecognisedboundary.typevalues. Existing callers always pass"sphere"or"box".- Position reads (
__reflect.get(entityId, "Transform", "position")) are tolerant of missing entities — if the predator/leader is despawned mid-frame the function returns zero for that contribution.