Input & Cursor

Overview

GridCursor2D is the primary input node for grid-based placement. It tracks cursor position on a grid, handles confirm/cancel input, and supports drag-to-place, keyboard navigation, and linear placement (fences/walls).

Scene Setup

1
2
3
4
5
MainLevel (Node2D)
├── TileMapLayer
├── GridCursor2D                    ← Add this node
│   └── PlacementPreviewLayer       ← Auto-created in _Ready()
└── MyGameBootstrap

Wire the cursor to your PlacementBootstrap:

1
2
3
4
5
6
7
8
9
[Export] public GridCursor2D? Cursor;
[Export] public GridPlacementProvider? Provider;

public override void _Ready()
{
    Provider!.Initialize();
    if (Cursor != null)
        Cursor.Context = Provider.Bootstrap;
}

Input Configuration

All input is driven by Input Map actions — no hardcoded keycodes. Create a CursorInputSettings resource:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
var settings = new CursorInputSettings
{
    ConfirmAction = "place",        // Left click / Enter
    CancelAction = "cancel",        // Right click / Escape
    UpAction = "ui_up",             // Keyboard directional
    DownAction = "ui_down",
    LeftAction = "ui_left",
    RightAction = "ui_right",
    EnableMouseInput = true,        // Track mouse position
    EnableDirectionalInput = true,  // Enable keyboard nav
    InitialRepeatDelay = 0.4f,      // Seconds before repeat starts
    RepeatDelay = 0.1f              // Seconds between repeats
};
Cursor.InputSettings = settings;

Define all actions in Project → Project Settings → Input Map.

Input Modes

Tap-to-Place (Mouse Click)

  1. Move mouse over grid — cursor tracks position
  2. Click (confirm action) → emits ConfirmPressed signal
  3. PlacementInputRouter routes to IPlacementInputBridge.ExecutePlacement()

Drag-to-Place (Hold and Drag)

  1. Press and hold confirm action → drag mode activates
  2. Move mouse across tiles → auto-places on each new tile
  3. Release confirm action → drag mode deactivates
1
2
3
4
5
6
Press Confirm → SetDragActive(true)
  → Move to new tile
  → _Process() detects tile change
  → Calls ExecutePlacement() for each new tile
  → Repeat while held
Release Confirm → SetDragActive(false)

Keyboard Directional

  1. Press directional action (up/down/left/right) → cursor moves one tile
  2. Hold → repeats after InitialRepeatDelay, then every RepeatDelay
  3. Works alongside mouse input

Linear Placement (Fences/Walls/Roads)

For placeables with IsLinear = true on ManipulationEntryData:

  1. Press confirm at start position → linear mode activates
  2. Move cursor to end position → path preview updates
  3. Release confirm → executes placement along entire path
1
2
3
4
5
6
7
Press Confirm at (0,0)
  → StartLinearPlacement(0,0)
  → Move to (5,0)
  → UpdateLinearPosition(5,0)
  → PreviewLayer shows path from (0,0) to (5,0)
Release Confirm
  → ExecuteLinearPlacement() → places all tiles on path

Key Properties

PropertyTypeDescription
InputSettingsCursorInputSettingsAll input action bindings
ContextPlacementBootstrapThe bootstrap providing the input bridge
CellSizefloatGrid cell size in pixels (default: 32)
GridOffsetVector2World-space offset of grid origin
SnapToGridboolSnap to nearest tile center (default: true)
GridPositionVector2ICurrent grid position (read-only)
IsDraggingboolWhether drag mode is active

Signals

SignalParametersWhen
GridPositionChangedVector2I gridPositionCursor moves to a different tile
ConfirmPressed(none)Confirm action tapped (not dragged)
CancelPressed(none)Cancel action pressed

PlacementInputRouter

PlacementInputRouter owns the signal → bridge routing. Created lazily by GridCursor2D.Router. It:

  1. Connects to ConfirmPressed → calls IPlacementInputBridge.ExecutePlacement()
  2. Connects to CancelPressed → calls IPlacementInputBridge.CancelPlacement()

No manual wiring needed — the router is created automatically when the cursor is added to the scene tree.

Headless Mode

For server-side or testing scenarios:

1
2
Cursor.SetHeadlessMode(true);  // Suspends all input processing
Cursor.SetHeadlessMode(false); // Re-enables input

Last Updated: 2026-06-01