Grid Targeting
Overview of the Grid Targeting subsystem and how cursor positions map to grid coordinates.
This page introduces the Grid Targeting subsystem used by the building workflow and explains how mouse/viewport positions become snapped grid coordinates other systems can use.
- API: GridTargetingSystem
- Related: GridTargetingState, GridPositioner2D, GridTargetingSettings
What it does
Section titled “What it does”Grid Targeting is responsible for turning pointer input into a stable, snapped target that the building and validation pipelines can consume.
v5.0.0 Architectural Improvement: Clear separation between positioning and targeting:
- GridPositioner2D: Pure input handling & positioning
- TargetingShapeCast2D: Dedicated collision detection
- IndicatorManager: Visual feedback & validation
v5.1.0 Target Resolution Enhancement: Smart target resolution through collision objects:
- Automatic Resolution: Collision objects now resolve to logical target nodes via metadata or Manipulatable components
- Flexible Targeting: Collision shapes can be on child nodes while targeting parent objects
- Metadata Support: Use
"root_node"metadata to specify target relationships - Manipulatable Integration: Automatically detects Manipulatable components for target resolution
Core Responsibilities:
- Convert viewport/world coordinates to grid-space
- Apply snapping rules (cell, edge, corner) and epsilon filtering to avoid flicker at boundaries
- Expose the current target info for other systems (validation, indicators, placement)
See also: Building System Process
Data flow
Section titled “Data flow”- Input is sampled from the active viewport (mouse/touch position)
- GridTargetingSystem computes a target using GridPositioner2D and settings from GridTargetingSettings
- Resulting target info is stored in GridTargetingState
- Downstream systems consume it:
- Indicators highlight the hovered cell(s)
- Placement validation evaluates rules against the target
- Building system uses it to place or preview objects
Target Resolution (v5.1.0+)
Section titled “Target Resolution (v5.1.0+)”In v5.1.0, the Grid Targeting system introduces intelligent target resolution that separates collision detection from logical targeting. When a collision object is detected, the system automatically resolves the appropriate target node using a priority-based search strategy.
Resolution Priority
Section titled “Resolution Priority”The system searches for the logical target in this order:
- Direct Metadata: Check the collision object for
"root_node"metadata- Accepts
Node2Ddirectly orNodePathto resolve
- Accepts
- Manipulatable Siblings: Search sibling nodes for Manipulatable components
- Uses
Manipulatable.rootas the target
- Uses
- Manipulatable Children: Search direct children for Manipulatable components
- Fallback: Use the collision object itself if no resolution found
Usage Examples
Section titled “Usage Examples”Example 1: Metadata-based resolution
# Collision shape is on a child node, but target should be parentvar collision_shape: CollisionShape2D = $CollisionShape2Dcollision_shape.set_meta("root_node", NodePath("..")) # Target parent nodeExample 2: Manipulatable-based resolution
# Collision area with Manipulatable childvar collision_area: Area2D = $BuildingAreavar manipulatable: Manipulatable = Manipulatable.new()manipulatable.root = self # Target is this nodecollision_area.add_child(manipulatable)Benefits
Section titled “Benefits”- Flexible Architecture: Collision shapes can be positioned independently of logical targets
- Clean Separation: Physics/collision concerns separated from game logic targeting
- Backward Compatible: Objects without metadata use the collision object as before
- Performance: Resolution happens only when collision objects change
Position calculation basics
Section titled “Position calculation basics”- World position → grid coords: sampling the world-space cursor and converting to integer cell coordinates based on configured cell size/origin.
- Snapping modes: center-of-cell, edge-aligned, or corner-snapping depending on your chosen settings.
- Epsilon filtering: small movement within a cell won’t constantly resignal target changes; this prevents UI flicker.
Consuming the current target
Section titled “Consuming the current target”Most systems don’t compute targeting directly—they read it from state or receive updates.
- Poll model/state: read the latest target info from GridTargetingState
- Event-driven: connect to target update signals emitted by your coordination layer
- Render-time: indicators query the state and render highlights for hovered cells
Typical consumers:
- Validation/Rules: evaluate PlacementValidator with the current target
- Indicators: TargetHighlighter and related components
- UI: hover tooltips or debug text using GridTargetingDebugText
- Object Info Display: TargetInformer shows information about targeted and manipulated objects
TargetInformer UI Component (v5.0.0+)
Section titled “TargetInformer UI Component (v5.0.0+)”The TargetInformer is a dynamic UI component that displays object information with intelligent priority handling across different game states.
Features
Section titled “Features”- Responsive to Targeting: Shows info immediately when hovering over objects via
GridTargetingState.target_changedsignal - Manipulation Priority: Automatically switches to show manipulated objects when manipulation is active
- Building Preview: Displays info for building preview objects during placement
- Graceful Fallbacks: Handles missing settings and null targets without errors
Priority System
Section titled “Priority System”The TargetInformer implements a three-tier priority system:
| Priority | Context | Signal Source | Use Case |
|---|---|---|---|
| 1. Highest | Manipulation | ManipulationState.active_target_node_changed | Shows info for objects being moved/demolished |
| 2. Middle | Building | BuildingState.preview_changed | Shows info for building preview objects |
| 3. Lowest | Targeting | GridTargetingState.target_changed | Shows info for hovered objects (passive targeting) |
Example Behavior:
# Scenario 1: Just hovering over objects# Player moves mouse over smithy → TargetInformer shows "Smithy" info
# Scenario 2: Start manipulation# Player selects smithy for move → TargetInformer switches to show manipulation info
# Scenario 3: Hover another object while manipulating# Player hovers over house while still moving smithy# TargetInformer STAYS on smithy (manipulation has priority)
# Scenario 4: End manipulation# Player completes move → TargetInformer returns to showing hovered objectThe TargetInformer connects automatically to all required signals when you call resolve_gb_dependencies():
var informer = TargetInformer.new()informer.info_parent = Control.new() # Container for info labelsadd_child(informer)informer.add_child(informer.info_parent)
# Connect to composition containerinformer.resolve_gb_dependencies(composition_container)# Now responds to targeting, manipulation, and building state changesConfiguration
Section titled “Configuration”The TargetInformer uses TargetInfoSettings for display formatting:
# These settings are typically configured in your GBSettings resourcetarget_info.position_format = "Position: (%s, %s)"target_info.position_decimals = 2
# The TargetInformer automatically uses these settings when available# Falls back to default formatting if settings are nullIntegration with Info Mode
Section titled “Integration with Info Mode”The TargetInformer works seamlessly with the Info mode toggle:
# When mode changes to INFOmode_state.current = GBEnums.Mode.INFO# TargetInformer becomes visible (if mode_state is connected)
# When mode changes to OFFmode_state.current = GBEnums.Mode.OFF# TargetInformer hides automaticallyCustom Object Names
Section titled “Custom Object Names”Override _to_string() on your object nodes to customize the display name:
# On your placeable object scriptfunc _to_string() -> String: return "Smithy (Level 2)" # Shows in TargetInformerConfiguration
Section titled “Configuration”Use GridTargetingSettings to control:
- Cell size and origin
- Snapping mode and tolerance (epsilon)
- Edge/corner behavior for non-centered snapping
These settings are typically provided via your composition container and shared with the GridPositioner2D.
Debugging and validation
Section titled “Debugging and validation”- Visualize: enable an indicator layer to color the active cell and neighbors.
- Inspect text: add GridTargetingDebugText to render current cell, world pos, and snap mode.
- Validate: run your rule set through PlacementValidator using the current target to confirm expected acceptance/rejection.
Visibility Behavior
Section titled “Visibility Behavior”The Grid Targeting system controls when the positioner is visible based on multiple interaction factors:
Basic Visibility Rules
Section titled “Basic Visibility Rules”- Mouse Input Enabled: When
enable_mouse_inputistrue, positioner follows mouse and respects all visibility settings - Mouse Input Disabled: When
enable_mouse_inputisfalse, positioner operates without mouse interaction and ignores mouse-specific visibility rules
Hide on Handled Behavior
Section titled “Hide on Handled Behavior”The hide_on_handled setting provides smart visibility management:
| Mouse Input | hide_on_handled | UI Element Hovered | Positioner Visible |
|---|---|---|---|
| ✅ Enabled | ✅ true | ❌ No | ✅ Yes |
| ✅ Enabled | ✅ true | ✅ Yes | ❌ No (hidden) |
| ✅ Enabled | ❌ false | ❌ No | ✅ Yes |
| ✅ Enabled | ❌ false | ✅ Yes | ✅ Yes (visible) |
| ❌ Disabled | ✅ true | Any | ✅ Yes (ignores UI) |
| ❌ Disabled | ❌ false | Any | ✅ Yes |
Practical Examples
Section titled “Practical Examples”# Example 1: Mouse-driven with auto-hidesettings.enable_mouse_input = truesettings.hide_on_handled = true# Result: Positioner follows mouse, hides when hovering UI elements
# Example 2: Keyboard-only modesettings.enable_mouse_input = falsesettings.hide_on_handled = true # This setting is ignored# Result: Positioner stays visible, controlled by keyboard/code
# Example 3: Always visible modesettings.enable_mouse_input = truesettings.hide_on_handled = false# Result: Positioner follows mouse, always visible even over UI