Grid Placement separates runtime logic from scene/game context. This is the canonical guide for host/session composition beyond the starter setup.
You usually interact with three layers:
GridPlacementHost— routes the active interaction and composes runtime services.PlacementSession— stores per-player/controller state and configuration references.- Context/provider adapters — connect the current level, player, and game facts to placement validation/services.
Default path
For new 6.0 scenes, use the host/session path directly:
- Add a
GridPlacementHostto the scene root or systems scene. - Assign
GridPlacementHost.grid_placement_bundle. - Assign each
GridPositioner2D.sessionto thePlacementSessionowned by that player/controller. - Author
PlacementLevelContextandPlacementOwnerin the scene.
When GridPositioner2D.session is assigned, the positioner configures itself from that session and registers it with the nearest GridPlacementHost at runtime. You do not need an injector for a normal new setup.
A normal project also does not need to instantiate every placement service manually. The host composes the configured service group from the bundle/session.
Explicit host configuration
Use explicit configuration when code owns the session lifecycle rather than a scene-authored positioner:
host.configure(session)For independent controllers:
host.register_session(controller_root, session, device_id)Unregister the controller/session when its owning scene or controller leaves. Keep one session per independent interaction; players that need separate selection, targeting, coordinate mode, or pending manipulation state should not share a session.
Consumers should use supported host/service entry points instead of depending on internal scene paths.
Compatibility: PlacementInjectorSystem
PlacementInjectorSystem remains a compatibility path for older/editor-composed scenes that still depend on injection. Do not choose it as the architecture for a new 6.0 integration.
If you maintain an injector-based scene:
- scope injection to the intended runtime subtree;
- keep the injector and host on the same
PlacementSession/bundle configuration; - do not let injector auto-registration compete with explicit
host.configure()orhost.register_session()ownership.
Prefer converging actively maintained scenes toward the host/session path instead of adding new injector dependencies.
Context injection
Use contexts/providers for facts that come from the current game scene, for example:
- target
TileMapLayerorGridMapsurface; - placed-object parent;
- placement owner/controller;
- game-owned reserved/occupied zones;
- inventory, cost, or world-state facts used by custom rules.
Do not copy those facts into long-lived plugin globals when their lifetime belongs to a level, session, or game system.
Shared placement world
Dimension-specific services still need to agree on the same committed placement world.
This matters for:
- GRID vs SMOOTH conflicts;
- CELL/EDGE/FACE/CORNER/TOP mount occupancy;
- SMOOTH socket occupancy;
- multiple sessions;
- save/restore.
Do not build a second occupancy registry that can commit conflicting placement state.
When to inject a custom service/provider
Add custom composition only when the project needs a real policy or integration point. Examples:
- a game-owned world-facts provider;
- custom placement validation rule;
- inventory/refund bridge;
- custom input/controller/session setup.
PlacementInjectable receives a PlacementSession directly. Do not write new integration code around removed PlacementContainer compatibility types.
Avoid replacing plugin services only to change naming or add logging.
Troubleshooting composition
Check these before changing plugin code:
GridPlacementHostexists in the relevant scene/systems tree.GridPositioner2D.sessionis assigned for scene-authored controller wiring.- The host and session use the intended
GridPlacementBundle. PlacementLevelContextandPlacementOwnerbelong to the active level/controller lifetime.- Only if maintaining a legacy injector scene: verify injector scope and registration ownership.