Development docs. This page describes the unreleased 6.3 build.
The placer is the node placement acts through: the character or controller
whose inventory, range, or cost rules apply. In 6.3 the PlacementSession
owns that value. Targeting, validation, commit, manipulation, reporting and
refund consumers all read it from the session rather than from a scene node.
Two ways to set it:
- Inspector (scene-authored setups). Assign
placer_rooton theGridPositioner2DorGridPositioner3Dthat already holds yoursessionand camera. The positioner writes it to the session when it becomes ready. - Code (controller-created, restored, headless, split-screen). Call
session.set_placer_root(node)yourself. Nothing else has to exist in the scene.
Reading it back:
var actor:Node = session.get_placer_root()# null == actor-freesession.get_placer_root() is the only authoritative answer. If you need the
node consumers act through — including the legacy adapter node — ask the
provider: session.get_contexts().owner.get_effective_owner().
Precedence
Both paths write the same session value, so the rule is about who may overwrite whom. The positioner never clobbers a binding it did not write.
| Situation | session.get_placer_root() |
|---|---|
Positioner has placer_root, session has nothing bound |
the positioner's node |
Code called set_placer_root(actor) first |
the code-bound node; a non-null positioner value does not replace it |
Positioner's placer_root is cleared |
cleared only when that exact node was what the session held |
Only a legacy PlacementOwner exists |
its owner_root, through the legacy adapter |
| Legacy node and an explicit binding, values differ | the explicit binding wins, and a runtime issue names both nodes |
| Nothing bound anywhere | null — actor-free (see Actor-Free Placement) |
A freed placer reads as null. Scene changes re-bind when the positioner
becomes ready again, so do not cache the placer across a level load: ask the
session each time.
Two sessions, two actors
Each session owns its own placer, which is what makes split-screen and
multiplayer work: one positioner per session, each with its own placer_root,
or one explicit set_placer_root call per session. The sessions may target the
same grid; the placer never crosses between them.
# Split-screen: each player controller owns its session.
player_one_session.set_placer_root(player_one)
player_two_session.set_placer_root(player_two)What the positioner does not decide
placer_root is not the camera and it is not the inventory owner. In 3D the
camera is a separate assignment on the positioner; in every dimension the
placer answers "who is acting", nothing more. Range, cost and unlock policy stay
in the rules your game enables.
Diagnostics
session.get_runtime_issues() reports a missing placer only when an enabled
rule needs one, and a legacy/explicit disagreement is reported by name:
No placer assigned but rule '<rule id>' requires one. Assign
GridPositioner2D/3D.placer_root or call PlacementSession.set_placer_root().
PlacementOwner 'PlacementOwner'.owner_root differs from the session placer
root 'Player'; the session binding wins. Remove the legacy PlacementOwner node
or clear one of the two.Using this in a game?
Grid Placement v6.3.0 — full source, playable demos, commercial license
The guide above is the same setup the shipped demos use. Try the build in your browser before you buy it.