Chris' Tutorials
Docs/Grid Placement

Grid Placement v6.0

Targeting Flow

How targeting works: positioner, shapecast, targeting state, and highlighting.

StatusDraft
Versionv6.0
UpdatedDevelopment docs generated from GDScript source

This is unreleased documentation in active development. APIs, class names, and behavior may change before the final release.

Draft — Unreleased:This page is in active development. APIs, class names, and behavior may change before the release is finalized. Use it as a preview of what's coming — not as a stable integration target.

This guide explains how targeting works in Grid Placement 6.0.0.

Version note: This guide is for the 2D TileMapLayer placement path in Grid Placement 6.0.0.


Main Pieces

Piece What it does
GridPositioner2D Tracks the cursor/controller target cell and snaps to the grid.
TargetingShapeCast2D Detects targetable objects for move/demolish/highlight workflows.
GridTargetingState Stores the active target map, positioner, target object, and targeting flags.
TargetHighlighter Optional visual feedback for the currently targeted object.
GridPlacementHost Reads the active session/targeting state while dispatching placement input.

GridPositioner2D

GridPositioner2D handles the visible grid cursor/target position.

It is responsible for:

  • Mouse follow: tracking the cursor and snapping to tile centers.
  • Keyboard/controller movement when configured.
  • Recenter behavior when entering placement modes.
  • Visibility logic based on mode, settings, and UI/input state.

It does not own shapecast collision detection or rotation/flip behavior.


TargetingShapeCast2D

TargetingShapeCast2D is the collision-query layer. Its job is to update GridTargetingState.target from the current physics collision result.

  • If GridTargetingState.is_manual_targeting_active is true, automatic target updates are skipped.
  • When a collider is found, the shape cast resolves the targetable root when possible.
  • When no collider is found, the current target is cleared.

Use this for object targeting, not for deciding whether a new object placement is valid. Placement validity belongs to placement rules.


GridTargetingState

The targeting state is the shared resource other systems consume.

Common fields:

var target: Node2D
var target_map: TileMapLayer
var maps: Array[TileMapLayer]
var positioner: Node2D
var is_manual_targeting_active: bool

PlacementLevelContext normally assigns target_map, and GridPositioner2D registers itself as the positioner.


Processing Flow

User input
→ GridPositioner2D moves to snapped tile center
→ TargetingShapeCast2D queries physics collisions at that center
→ GridTargetingState.target updates with the found object, if any
→ placement, manipulation, or UI reads the targeting state

Essential Setup for Plugin Users

If targeting looks broken, check these first:

  • PlacementLevelContext.target_map is assigned.
  • GridPositioner2D was injected or registered successfully.
  • TargetingShapeCast2D was injected or registered successfully.
  • Your shapecast collision mask matches the targetable layers.
  • The mode/settings allow the positioner to stay active/visible.
  • Your UI is not consuming the input path you expect.

Collision Layer vs Collision Mask for Targeting

TargetingShapeCast2D uses Godot's collision system to detect objects.

Property What it means Used by ShapeCast2D to...
collision_mask on TargetingShapeCast2D Which layers to check Find objects.
collision_layer on target objects Which layers the object is on Be detectable by matching masks.

Detection works only when the shapecast mask overlaps the target object's layer.

TargetingShapeCast2D:
  collision_mask: includes layer 10

Placed object:
  collision_layer: includes layer 10

Common mistake: setting only collision_mask = 10 on placed objects. That lets the object detect layer 10 things, but it does not put the object on layer 10. The shapecast cannot find it unless the object's collision_layer includes the target layer.


Avoid Duplicate Targeting Layers

Only the intended target/root should own the targeting layer.

# Good: one clear target owner
GeyserPumpRoot:
  collision_layer: includes target layer
  StaticBody2D:
    collision_layer: does not include target layer

# Risky: multiple target owners
GeyserPumpRoot:
  collision_layer: includes target layer
  StaticBody2D:
    collision_layer: also includes target layer

When multiple nodes in one scene have the targeting layer, the shapecast may report a child collider instead of the root you intended.


ShapeCast Collision Flags

Godot ShapeCast2D collision flags matter.

TargetingShapeCast2D:
  collide_with_areas: true
  collide_with_bodies: true

Use collide_with_areas = true when your target objects are Area2D. Use collide_with_bodies = true when your target objects are StaticBody2D or other physics bodies. Many placed objects use bodies, so leaving collide_with_bodies false is a common cause of move/demolish/highlight failing.

Quick diagnostic:

print("TargetingShapeCast2D: areas=%s, bodies=%s" % [
    $TargetingShapeCast2D.collide_with_areas,
    $TargetingShapeCast2D.collide_with_bodies
])

TargetingShapeCast2D.debug_log_collisions can also be enabled for verbose collision logging when a logger is available.


Preview Stability and Manual Targeting

During active building/manipulation flows:

  • TargetingShapeCast2D can stop auto-updating when manual targeting is active.
  • Collision exclusions can prevent previews from blocking their own placement validation.
  • Manipulation visuals are handled by ManipulationParent, not the positioner.

Common Targeting Problems

  • Positioner never appears
    • Check mode/state injection.
    • Check targeting settings visibility rules.
  • Target object never updates / move-demolish-highlight does not work
    • Check collide_with_areas and collide_with_bodies first.
    • Check TargetingShapeCast2D.collision_mask matches your targeting layer.
    • Check that the target object has collision_layer including that same layer.
    • Enable TargetingShapeCast2D.debug_log_collisions for diagnostics.
  • Cursor does not move
    • Check GridPositioner2D input settings and target map wiring.
  • Move/build targeting acts frozen
    • Check whether manual targeting is intentionally active.

Related Guides

Source

docs/v6-0/guides/targeting-flow.md

Plugin docs root:gdscript/plugins/grid_placement_dev/docs