Placement rules decide whether a placement action is allowed.
Use this guide when you want to understand why placement succeeds/fails, where to put rules, and how rules connect to object placement, terrain, inventory costs, and manipulation.
Version note: This guide is for Grid Placement 6.0.0. For custom rule authoring details, see Custom Placement Rules. For startup/readiness checks, see Validation: Configuration.
What a Rule Does
A placement rule is a Resource that participates in the runtime placement loop.
A rule can:
- Inspect the active placement target.
- Return pass/fail validation with a user-facing issue.
- Mark one or more indicators invalid.
- Run side effects after a successful placement, such as spending materials.
Rules are not just editor settings. They run while the player is trying to place something.
Beginner Mental Model
selected object/terrain
→ preview appears
→ rules validate the current target
→ UI shows valid/invalid feedback
→ player confirms
→ rules validate again
→ successful placement mutates the world
A valid preview is not a completed placement. Spend inventory, save state, achievements, and other irreversible effects should happen only after successful placement.
Rule Types
| Type | Use for |
|---|---|
PlacementRule |
General rules that do not need per-tile indicators, such as inventory/material checks. |
TileCheckRule |
Rules that depend on tile/indicator positions, such as bounds, collisions, or tile custom data. |
RuleResult |
The pass/fail result returned by rule validation. |
If your rule needs to know which cells an object covers, it probably belongs on TileCheckRule. If your rule checks player state, inventory, unlocks, or quest state, it can usually extend PlacementRule directly.
Where Rules Come From
Grid Placement 6.0 combines rules from multiple places.
| Source | Scope | Use for |
|---|---|---|
PlacementSettings.placement_rules |
Global/default rules | Rules every placement should usually follow, such as bounds or collision checks. |
PlacementProfile.placement_rules |
Category/profile rules | Shared behavior for groups such as buildings, fences, decorations, or doodads. |
ScenePlacementEntry.placement_rules |
One object entry | Special rules for one object, such as "must be near water". |
The effective rule set is resolved when the object/terrain workflow prepares its preview and validation state.
Ignoring Base Rules
Some objects intentionally need different rules.
Examples:
- A decorative flower should be free-form and overlap allowed.
- A debug/test object should ignore normal restrictions.
- A fence might use line placement and adjacency rules instead of building rules.
Use ignore_base_rules carefully:
| Setting | Effect |
|---|---|
PlacementProfile.ignore_base_rules |
Objects with this profile can skip global/base rules. |
ScenePlacementEntry.ignore_base_rules |
This specific entry can skip inherited rule tiers. |
If multiple profiles are present and any profile ignores base rules, the object can bypass the global base rules. Keep profile names clear so designers understand why an object behaves differently.
Built-In Rules Plugin Users Should Know
| Rule | Purpose |
|---|---|
WithinTileMapBoundsRule |
Keeps placement inside valid cells of the active TileMapLayer. |
CollisionsCheckRule |
Checks whether placement overlaps physics objects according to the configured masks. |
ValidPlacementTileRule |
Checks tile custom data or tile validity expectations. |
SpendMaterialsRuleById |
Canonical id-keyed cost rule for spending materials after successful validation. |
SpendMaterialsRuleGeneric |
Legacy ResourceStack-keyed spend rule kept for existing projects. Prefer SpendMaterialsRuleById for new work. |
Collision Shapes and Indicators
Tile-based rules need positions to check. Those positions usually come from collision shapes on the object scene.
Recommended object shape:
ObjectRoot (Node2D)
├── Sprite2D
└── StaticBody2D or Area2D
└── CollisionShape2D
The pipeline is roughly:
placeable scene
→ preview instance
→ collision shapes are inspected
→ covered tile positions are calculated
→ rule indicators are created
→ TileCheckRule rules validate those indicators
If a placeable has no collision shapes, some tile-based rules may have no tile positions to evaluate. That can be intentional for purely visual objects, but it is surprising for buildings. Add collision shapes when you want reliable bounds, collision, and tile-data validation.
Collision Layer vs Collision Mask
This is the most common source of confusing rule behavior.
- collision_layer = what the object is.
- collision_mask = what the detector or rule looks for.
For CollisionsCheckRule to detect an existing object, the existing object must be on a collision layer that the rule's mask checks.
Existing object:
collision_layer includes layer 10
Collision rule / shapecast:
collision_mask includes layer 10
If the object is only on layer 1 but the rule checks layer 10, the rule will not see it.
Placement vs Manipulation Rules
Placement and manipulation share much of the validation infrastructure, but their rule sources differ.
| Workflow | Rule source |
|---|---|
| New object placement | Rules from settings, profiles, and ScenePlacementEntry. |
| Move/manipulation | Rules from the selected Manipulatable / manipulation settings path. |
| Terrain painting | Terrain placement rules and tile/terrain validation path. |
For manipulation, make sure the object has a Manipulatable component and targeting can detect the object. A perfectly written rule will not help if the object cannot be targeted.
Inventory and Cost Rules
Preferred: SpendMaterialsRuleById
Use SpendMaterialsRuleById for new projects.
It uses a Dictionary[StringName, int] of item id to amount, for example:
{
&"Wood": 10,
&"Stone": 4,
}
The located inventory/container bridge should implement:
| Method | Purpose |
|---|---|
get_count_by_id(id: StringName) -> int |
Check whether enough material exists during validation. |
try_remove_by_id(id: StringName, amount: int) -> int |
Spend after successful validation. |
try_add_by_id(id: StringName, amount: int) -> int |
Roll back partial spends when needed. |
The rule should own the spend. Do not also subtract materials in a success signal handler, or you will double-spend.
Legacy: SpendMaterialsRuleGeneric
SpendMaterialsRuleGeneric is the older ResourceStack-keyed rule. Keep it for existing projects that already depend on it. For new projects, prefer the id-keyed rule so your placement costs do not depend on a specific inventory resource class.
Locating an Inventory Container
Spend/refund rules locate an inventory bridge from the placement owner.
Search root is normally:
PlacementOwner.owner_root
The locator does not magically search every autoload or every node in the scene tree. Put a bridge node under the owner root, or intentionally point owner_root at a node that can reach the bridge.
Common locator methods include matching by:
- Node name.
- Script file name.
- Godot group.
Group-based lookup is often the most refactor-friendly option.
Don't Duplicate Rule Logic in UI
Your UI should display rule results, not re-implement them.
Good UI behavior:
- Read the placement report/action data.
- Show the first issue or list of issues.
- Refresh inventory display after placement succeeds.
- Keep buttons disabled when the session says the entry is unavailable.
Bad UI behavior:
- Rechecking collision rules separately.
- Spending inventory in both the rule and a success signal.
- Assuming a green-looking preview means the world has already changed.
Custom Rule Authoring Checklist
When writing a custom rule:
- Choose
PlacementRuleorTileCheckRulebased on whether you need tile/indicator positions. - Override the validation method expected by that base class.
- Return clear issue text for failure cases.
- Keep irreversible side effects in the post-success/apply path.
- Call the base setup/runtime issue methods when extending an existing rule.
- Test both success and failure cases.
See Custom Placement Rules for detailed examples.
Practical Guidance
- Put project-wide defaults in
PlacementSettings.placement_rules. - Put category behavior in
PlacementProfile. - Put unique object requirements on the
ScenePlacementEntry. - Use collision shapes for buildings and other objects with a footprint.
- Use
SpendMaterialsRuleByIdfor new material-cost rules. - Keep rule failure messages beginner-readable; they often become player-facing UI text.
Related Guides
- Custom Placement Rules
- Placement Workflow
- Validation: Configuration
- Refund on Demolish
- Targeting Flow
- Web Export Guide
Source
docs/v6-0/guides/placement-rules.md
Plugin docs root:gdscript/plugins/grid_placement_dev/docs