Curitor
Gameplay Systems · Topic Guide

Building & Placement

Equip a building tool, select a placeable item, and a ghost previews where it will land — green where it fits, red where it doesn't. Confirm and it stands in the world for everyone, pickable again only by whoever placed it.

What ships

BuildModeController turns build mode on for a player the moment they are holding a building tool with a placeable item selected in the hotbar. While it is active it shows a ghost of the selected item at the aimed, grid- snapped spot, tinted by the same validity check the commit itself runs — so a ghost reading green can never be refused a frame later by different math. Confirming spawns the object through WorldPlacementSystem, which replicates it to every machine and remembers it well enough to restore it on load.

The ghost tints green where the spot validates and red where it doesn't — same check, same frame, no surprise refusal.

The Building Hammer gate

There is exactly one gate, and it is a type test rather than a capability flag: equipment.GetEquipped(hand) is BuildingToolDefinition, checked on either hand. It decides both halves of building — BuildModeController reads it to enter build mode, and PlacedObject.CanPickUp reads it again before letting anyone take a placement back. Nothing adds a bool to an item class to grant these verbs: make a BuildingToolDefinition asset of your own and equipping it grants both automatically.

Making a placeable item

Any item becomes buildable by being a PlaceableDefinition instead of a plain ItemDefinition — nothing else marks it. Its fields describe how it previews and settles into the world:

FieldTypeDefaultWhat it does
placedModelGameObjectThe model shown once placed, distinct from worldModel (the loose drop/pickup look). Leave empty to reuse worldModel.
footprintVector3Half-extents of the box that must be clear for placement to be valid. Leave at zero to derive it from the placed model's renderer bounds instead — set it by hand only when the visual bounds lie.
snapToGridbooltrueSnap the ghost and the placed object to the world grid from BuildingConfig. Off allows free placement for props where grid alignment would look mechanical.
allowRotationbooltrueAllow the player to rotate the ghost before committing. Off for objects whose orientation is meaningless or must stay fixed.
surfaceSlopeLimitDegfloat40Steepest surface this item may be placed on, in degrees. Defaults to the same number as the motor's walkable limit: what a player can stand on, they can build on.
throwableWhenPlacedboolfalseAlso makes the placed copy pick-up-and-throwable — see Placeables that can be thrown, below.

Author it from Create → Party Core Kit → Inventory → Placeable Item Definition, fill in the ordinary item fields (display name, icon, world model) plus the placement fields above, and it is immediately selectable in the hotbar while a building tool is equipped.

Placeables that can be thrown

Setting Throwable When Placed on a PlaceableDefinitionlets the same item serve as both a building piece and a throwable weapon — a crate placed with the hammer that a player can also pick up and hurl, the way a build-and-defend map and a physics-golf map both wanted the same asset to behave. Placing one with the flag on adds the stock throwable crate's own recipe on top of the ordinary placement collider: a Rigidbody, ThrownDamageSource and ChargeThrowInteractable. The trade is explicit — the placement keeps live physics and can be carried off from the moment it's placed, so it stops counting as fixed geometry the instant someone throws it. It is off by default, so every existing placeable ships unchanged.

Pickup keeps working after a throw with no extra bookkeeping: PlacedObject.CanPickUp is keyed by the object — tool plus owner id — never by a cached position, and a save taken after a throw records wherever the object actually rests, because it reads the live transform rather than a stored field.

Grid, reach and the ghost

BuildingConfig holds every placement-feel knob in one asset — Install creates it at Building/BuildingConfig.asset, and editing it there changes how placement feels for every player at once:

FieldTypeDefaultWhat it does
gridCellSizefloat0.5Edge length of one world-grid cell that snapping placeables align to.
rotationStepDegfloat90Degrees the ghost turns per rotate press (R, or a gamepad shoulder button).
maxPlacementDistancefloat4How far from the character the aim ray still finds a placement spot.
aimLayerMaskLayerMaskLayers the placement aim ray tests for a surface to build on.
ghostValidColor / ghostInvalidColorColorGhost tint while the spot is valid or refused.
ghostValidMaterial / ghostInvalidMaterialMaterialThe actual materials the ghost wears. Install fills these with its own transparent URP Lit materials tinted from the colors above, and only fills them when empty — assign your own to take ownership.

Building from code — the only network-safe entry point

Gameplay code that needs to place something programmatically — scripted construction, a bot builder — must call PlacedObjectFactory.Place, never PlacedObjectFactory.SpawnLocal directly.

  1. Place routes through the network when a session is live

    While a PlacementReplicator is present, Place hands the request to it and every connected machine builds its own copy off the resulting broadcast — including the machine that asked.
  2. SpawnLocal builds on the calling machine only

    Calling it directly builds the object right there and nowhere else. Online, nobody else ever sees the result — it exists on exactly the one machine that called it.
  3. Place falls back automatically offline

    With no replicator installed, Place calls SpawnLocal itself, so the same call is correct in every game mode without a branch in your code.

Building without code: two scripting nodes

A level rule that doesn't warrant a script can reach the same placement path through two stock nodes, both routing through the identical WorldPlacementSystem / PlacedObjectFactory construction real building does, so a scripted placement looks, saves and picks up exactly like one a player built by hand.

FieldTypeWhat it does
Place ObjectInstructionPlaces a copy of a placeable item in front of the rule's subject. No overlap or reach check runs — a scripted spawn is authored trust, the same posture as a dropped item — so place it somewhere sensible. Becomes a no-op if the subject has no resolvable player id, since an ownerless placement could never be picked back up.
Remove Placed ObjectInstructionRemoves the placed object the rule lives on (or lives under) the clean way: its placement-ledger row is pruned first, so a late joiner never resurrects it. Use this instead of Destroy Self on a placed object's own rule — Destroy Self alone would leave that ledger row behind.

Picking a placement back up

A placed object stays pickable only by the player who placed it, and only while that player holds a building tool — the same type gate that unlocked placing it. PlacedObject.CanPickUp checks both: the interactor holds a BuildingToolDefinitionin either hand, and its resolved player id matches the placement's owner. Anyone else sees the object but gets no prompt and no verb at all.

Persistence

Placed objects are spawned at runtime, so there is nothing in the scene file to restore onto — the same situation a dropped item is in. WorldPlacementStore saves a recipe per placement (item id, position, yaw, owner id), never an identity, and a load clears whatever stands now before rebuilding what was saved. Restoring goes back through the same WorldPlacementSystem.RequestPlaceseam a player's commit uses, so a host's load reaches every client exactly like a placement would.

Multiplayer