Table of Contents

Spatial Architecture Overview

One spatial index — the per-cell cluster broadphase — the grid it stands on, and which feature page answers which question.

Status: ✅ Implemented · Visibility: Public · Level: 🔵 Core · Category: Spatial

Start with: What Spatial Costs You

🎯 What it solves

Landing on the Spatial category for the first time is disorienting: every feature page assumes you already know how the pieces relate. This page is the map — what exists, what each piece is for, and which feature to read next depending on what you are trying to do. It is a map, not a starting point: read What Spatial Costs You first, because it teaches what a spatial archetype signs you up for, and this page then tells you where each of those pieces is documented.

⚙️ How it works (in brief)

There is exactly one spatial index, and it is the per-cell cluster index. Every query shape — box, radius, ray, frustum and k-nearest — resolves through it. A component carrying [SpatialIndex] allocates no spatial storage segment of its own; the attribute contributes field metadata (offset, shape, mode, category) and nothing else. A reflection test walks the whole engine assembly to hold that true, rather than a text search over the source.

Two layers make that one index work, and they are levels of the same structure rather than alternatives:

  1. The cell grid — one coordinate grid per realm (realm 0's configured once via Spatial Grid Configuration & Tier Control, any other realm's through Realms). It is sparse and three-level, so empty regions cost nothing, and it answers coarse questions cheaply per cell instead of per entity. It underpins both the index and the dispatch features: Spatially-Coherent Entity Clustering (every entity in a cluster shares one grid cell), Tiered Simulation Dispatch (per-cell simulation frequency), and Checkerboard Dispatch (safe parallelism on top of tiering).
  2. The per-cell cluster broadphase — inside each occupied cell, a linear array of cluster bounding boxes per archetype, split into a static and a dynamic half. A query expands into the cells it overlaps, scans those boxes, and tests entities individually only in the clusters that overlap. See Cluster Spatial Queries for the mechanism and Spatial Query API for the surface that reaches it.

Storage mode does not select a query path. Every archetype is cluster-backed, whatever its components' storage modes, so storage mode decides only where component data sits inside the cluster — a Versioned component keeps its HEAD in the cluster slot and its revision chain separate. It decides nothing about which structure answers a query, because there is only one.

The grid is therefore required, not optional. An archetype that declares a [SpatialIndex] field and finds no configured grid throws InvalidOperationException at InitializeArchetypes, naming the archetype and telling you to call ConfigureSpatialGrid during startup or drop the attribute. It fails at startup on purpose, while you can still act on it, rather than at the first spawn. Every spatial example in the guide calls ConfigureSpatialGrid for this reason.

Decision table

You want to... Read
Understand what a spatial archetype costs per tick, and choose a cell size What Spatial Costs You
Query "what's near this point / in this box / along this ray" Field Attribute & Schema Integration → Spatial Query API
Simulate a large world at multiple frequencies (near the player vs. far away) Spatial Grid Configuration & Tier Control → Tiered Simulation Dispatch
Keep spatially-nearby entities in the same cluster for cheap per-cell operations Spatially-Coherent Entity Clustering (requires the grid, above)
Separate rarely-moving from every-tick data so maintenance skips the former Static / Dynamic Separation
Skip whole clusters by a bitmask before geometry tests Category Filtering
Change a parameter because tick time or query cost moved Tuning the Spatial Grid, after Reading Spatial Telemetry

⚠️ Guarantees & limits

  • One index — the per-cell cluster broadphase serves every spatial query, and it is the only index home. Nothing bypasses the grid, and no configuration adds a second structure alongside it.
  • [SpatialIndex] alone is not sufficient — it requires ConfigureSpatialGrid to have been called before InitializeArchetypes, and registering it never configures the grid implicitly. The failure is a startup exception naming the archetype, not a degraded query.
  • The grid is per realm, never per archetype — within a realm every spatial archetype shares one cell size and one set of world bounds, fixed at registration and immutable afterwards. Several realms are several isolated worlds, each with its own grid (Realms); an application that never names a realm has one, realm 0.
  • Two cellSize parameters exist, but only one shapes queries — the realm's SpatialGridConfig.CellSize. The cellSize argument on [SpatialIndex] is carried in schema metadata and sizes no live structure.
  • The world is bounded — positions outside the configured bounds are clamped into the nearest edge cell rather than rejected. See What Spatial Costs You for what that means in practice.
  • This page describes architecture, not an API surface of its own — there is nothing here to call; every code example lives on the linked feature pages.