Spatial
The @shell/spatial package provides 2D flow-field navigation, a spatial grid, dynamic obstacles,
terrain costs, range-gated steering, and agent separation.
@shell/spatial is a game-level package. It is not included in @shell/engine-2d, so install it
separately even when your application already uses the 2D engine:
pnpm add @shell/spatialAdd SpatialPlugin to the root world and configure the navigable world bounds:
import { Engine2D, World } from '@shell/engine-2d';import { SpatialPlugin } from '@shell/spatial';
const world = new World();world.addPlugin( Engine2D({ canvas, resizeTo: window }), SpatialPlugin.config({ cellSize: 32, width: 2048, height: 2048, }),);
await world.mount();Keep the aggregate plugin on the root world when using scenes. Its resources are shared with child scenes, while scene-owned targets and obstacles are added and removed as scenes mount and unmount.
Configuration
Section titled “Configuration”| Option | Default | Description |
|---|---|---|
cellSize | 64 | Width and height of one navigation and spatial-index cell |
width | 4096 | Navigable world width, starting at world coordinate 0 |
height | 4096 | Navigable world height, starting at world coordinate 0 |
flowFieldMaxHeapPopsPerUpdate | 4096 | Shared amount of flow-field build work allowed per Update |
flowFieldMaxFieldsStartedPerUpdate | 4 | Maximum number of new flow-field builds started per Update |
lineOfSightIntervalMs | 100 | Approximate time over which every range-gated agent is checked once |
Choose a cellSize that reflects the useful navigation resolution of the game. Smaller cells
produce more precise routes but increase the size and rebuild cost of every navigation profile.
Move an agent toward a target
Section titled “Move an agent toward a target”A target needs FlowFieldTarget and either a Position or an explicitly assigned GridCell. An
agent needs Position, Velocity, GridCell, and FlowFieldAgent:
import { Define, Position, Velocity } from '@shell/engine-2d';import { FlowFieldAgent, FlowFieldTarget, GridCell } from '@shell/spatial';
const Target = Define.prefab().with(Position, { x: 1600, y: 900 }).with(FlowFieldTarget);
const Agent = Define.prefab() .with(Position, { x: 100, y: 100 }) .with(Velocity) .with(GridCell) .with(FlowFieldAgent, { speed: 120 });
const commands = world.get('commands');commands.spawn(Target);commands.spawn(Agent);FlowFieldTarget automatically adds its required GridCell. For a positioned target, the plugin
keeps that cell synchronized. The agent’s velocity is steered toward the target during fixed updates,
and the kinematics dependency installed by SpatialPlugin integrates that velocity into Position.
Flow-field construction is budgeted, so a newly created or changed field can take multiple updates to publish in a large world. Agents keep using the last completed field while a replacement builds.
What the aggregate installs
Section titled “What the aggregate installs”| Plugin | Purpose |
|---|---|
GridPlugin | Synchronizes GridCell and exposes the spatialGrid resource |
FlowfieldPlugin | Tracks targets, builds flow fields, and steers agent velocity |
ObstaclePlugin | Maps positioned obstacles and terrain costs onto the default profile |
LineOfSightPlugin | Optionally gates steering by target range and chooses priority groups |
SeparationPlugin | Adds local repulsion between participating agents |
KinematicPlugin | Integrates the velocity changed by flow-field and separation steering |
The package also exports these plugins individually. Use the aggregate for normal navigation; use a
standalone plugin when you only need one capability, such as GridPlugin.
Topics
Section titled “Topics”- Flow Fields — Create agents and targets, use groups, and access navigation profiles
- Obstacles & Terrain — Block cells or make regions more expensive to cross
- Agent Behaviors — Gate steering by range, select fallback targets, and separate crowds
- Spatial Grid — Find entities by cell or world-space bounds, with or without navigation