feat: Enhance selection and grid functionality in sandbox stage and gizmos

This commit is contained in:
2026-08-29 00:18:57 -04:00
parent 2a6b4aa965
commit badff571f0
5 changed files with 128 additions and 55 deletions
+21 -9
View File
@@ -553,9 +553,15 @@ assembled in a "Whole Stickman" preview that supports translation, rotation, and
**Grid/snap:** an optional `StageGrid` overlay (drawn behind `World`) plus `Grid`/`Snap`
`CheckBox` toggles and a `Size` `SpinBox`, persisted to `user://sandbox_settings.json`
(`grid_size`/`snap_to_grid`/`show_grid`); snapping rounds the placement cursor and translate
drags via `_snap_to_grid()` / `StageGizmos.snap_size`. UI built in code (CanvasLayer +
drags via `_snap_to_grid()` / `StageGizmos.snap_size`. The grid is visible only in EDIT mode
(`_apply_grid_settings()` sets `_grid.visible = _show_grid and current_mode == EDIT`, re-applied
on mode change). **Touchpad:** `_input` handles `InputEventMagnifyGesture` (pinch zoom ×factor)
and `InputEventPanGesture` (two-finger pan, `delta × 3 / zoom`) alongside the mouse wheel/middle
pan. UI built in code (CanvasLayer +
PanelContainer top bar): mode toggle, six palette buttons (built from the spawner registry),
Grid/Snap/Size controls, status label.
Grid/Snap/Size controls, status label. The build controls (spawn palette + Grid/Snap/Size) are
hidden in PLAY via `_set_build_controls_visible()`, called from `_enter_edit_mode()` /
`_enter_play_mode()` (the mode toggle and status label stay visible).
- `scripts/stage_spawner.gd` — `class_name StageSpawner`, `extends RefCounted`; registry-driven
spawner (Phase 2). A `_registry: Array[Dictionary]` maps ids to terrain/prop/stickman templates;
adding a type = appending an entry (no hard-coded id `match`). Reuses `TerrainUtils.spawn_block`,
@@ -577,13 +583,19 @@ assembled in a "Whole Stickman" preview that supports translation, rotation, and
`clear_selection`/`select_only`/`add_to_selection`/`toggle_selection`/`is_selected`/`hit_test`/
`update_hover`/`box_select`.
- `scripts/stage_gizmos.gd` — `class_name StageGizmos`, `extends Node2D`; hover highlight +
selection outline + a rotate ring handle (Phase 2). **No translate handle** — objects are
dragged directly by the root (`begin_translate_drag(node, world_pos)` sets the target + starts
a TRANSLATE drag; `drag_to` drives `global_position` with optional grid snap via `snap_size`).
Pure `_draw()` + distance-based hit-testing (no `Area2D`); `enum Handle { NONE, TRANSLATE,
ROTATE }`. Rotation drives `global_rotation` about the AABB center. `transform_committed(node)`
emitted on drag end. `set_enabled(false)` hides + disables in PLAY. Also draws the box-select
marquee via `set_box_rect(rect)`.
selection outlines + a rotate ring (Phase 2). **No translate handle** — objects are dragged
directly by the root; `set_targets(nodes: Array[Node2D])` holds the current multi-selection,
and `begin_translate_drag(node, world_pos)` starts a TRANSLATE drag that moves **all** selected
nodes together (`drag_to` drives `global_position` with optional grid snap via `snap_size`,
snapping the drag anchor and applying the same delta to the rest). The **rotate ring** is drawn
and hit-tested only when exactly one node is selected (`hit_test` → `Handle.ROTATE`).
The rotate drag is **absolute-from-start** — `drag_to()` sets `global_rotation` to
`_rotate_start_rotation + (current start angle)`, with holding **Ctrl** snapping to
15° increments via `snappedf(new_rotation, deg_to_rad(15.0))`.
`_draw()` draws a selection outline for **every** selected node. Pure `_draw()` +
distance-based hit-testing (no `Area2D`); `enum Handle { NONE, TRANSLATE, ROTATE }`.
`transform_committed(nodes: Array[Node2D])` emitted on drag end. `set_enabled(false)` hides +
disables in PLAY. Also draws the box-select marquee via `set_box_rect(rect)`.
- `scripts/stage_grid.gd` — `class_name StageGrid`, `extends Node2D`; the optional world-space
grid overlay (Phase 2). Pure `_draw()`: grid lines pan/zoom with the camera (`camera.get_screen_center_position()`
+ viewport extent / zoom), with a heavier **major line every 5 cells**; `grid_size` / `enabled`