Builtin Library
- ..
- README.md3.9 KB
- astar.luau6.9 KB
- graph.luau10.9 KB
- init.luau14.4 KB
- links.luau6.1 KB
- modifiers.luau8.7 KB
- path.luau6.9 KB
README.md
pathfinding.module
Public API for the pathfinding package. Caller-facing entry point lives
in init.luau; sibling modules implement individual concerns.
Modules
| File | Purpose |
|---|---|
init.luau | Public surface (path, addLink, addModifier, registries, costs). |
astar.luau | Generic A* over an adjacent(node) -> { Edge } callback. Binary-heap frontier, opaque node identity via nodeKey. |
graph.luau | Two concrete graphs: grid (uniform 3D voxel grid) and waypoint (hand-authored network). Both expose the same interface. |
modifiers.luau | Volumetric cost-zone registry. AABB containment, OBB hooks reserved for future engine OBB primitive. |
links.luau | Off-mesh link registry + per-query baking onto a graph. |
path.luau | Waypoint / Path constructors. Mirrors Roblox PathWaypoint shape. |
Algorithm at a glance
nodeAt(start)/nodeAt(goal)— snap world points onto graph nodes.links.bake(graph, agent)— snap every enabled, condition-satisfying link's endpoints onto graph node-keys; bucket adjacency lookups by from-key. One pass perpathcall, not per A* expansion.- A* expands each node, calls the closure
adjacent(node). The closure:- asks the graph for native neighbours,
- for each neighbour, queries
modifiers.costAt(midpoint, costs, mode), - skips edges with
math.hugemultipliers, - appends link-overlay edges from
linkBuckets[nodeKey].
- On reach:
Path.fromAStar(result, graph, edgeMeta)produces a list of{position, action, label, link, payload}waypoints. Walk edges becomeaction = "Walk"; link edges becomeaction = "Custom"withlabel = link.label.
Edge-cost composition
Per edge e: a -> b:
edge_cost = base_cost(a, b) * compose({ modifier.cost-at-mid(a, b) }, mode)
Composition mode:
multiply:cost *= Π c_i(default; composable preferences amplify).max:cost *= max(c_i)(worst-zone wins; conservative).first:cost *= c_first(registration order; legacy).
math.huge from any modifier blocks the edge regardless of mode. The
per-edge midpoint sample matches Roblox's PathfindingModifier semantics
exactly — multiple overlapping modifiers compose.
Identity & determinism
- A* node identity is by string key (
graph.nodeKey(node)), not table identity, so transient nodes built bynodeAtsnap onto the same key as graph-internal nodes. - The frontier is a binary min-heap; ties on
fare broken by insertion order. With identical inputs the same path is returned — important for tests and replay.
Extension points
graph.luauexposes a clear interface (nodeAt,nodeKey,position,adjacent,heuristic,edgeMidpoint). Future navmesh-polygon graphs implement the same shape; nothing in the rest of the package cares.modifiers.luaualready exposes abasisfield for oriented bounding boxes; the AABB-containment fast path is the current default. When an OBB primitive lands (#2677-style attachment basis or a future renderer-side OBB) the basis-aware branch inmodifiers.containsis the single place to extend.links.luau::bakebuilds the per-query overlay; if predicate links start showing up in profiles, this is the cache point.
Boundaries
- This module performs no engine I/O: no physics raycasts, no entity spawning, no renderer state, no time. Pure data in, path out.
- That means the package never breaks the engine, and tests can
exercise it without booting the engine — see
astar.luau::searchgraph.gridfor the canonical setup.
- The components (
PathfindingLink.component,PathfindingModifier.component) own the entity-side wiring and call_addLinkFromComponent/_addModifierFromComponentinawake. They're a thin, optional adapter on top of the module.