Add sandbox stage builder scripts and functionality

- Introduced StageGizmos for hover highlighting, selection outlines, and rotation handles in the sandbox stage builder.
- Added StageGrid for an optional world-space grid overlay that adjusts with camera panning and zooming.
- Implemented StageSelection for geometric hit-testing and selection management of nodes in the sandbox.
- Created StageSpawner as a registry-driven factory for spawning terrain, props, and stickmen, allowing for dynamic template management.
- Each script includes necessary constants, state management, and public API methods for interaction.
This commit is contained in:
2026-08-28 21:53:55 -04:00
parent 0e971d99b1
commit ef30931b20
17 changed files with 1800 additions and 6 deletions
+302
View File
@@ -0,0 +1,302 @@
# Sandbox Stage Builder
## 1. Overview & Objective
**This Phase (Phase 2)** builds the foundation for the kidfriendly director sandbox: a **visual stage** where users can place terrain, props, and stickmen using a simple palette, then switch between **Edit Mode** (building) and **Play Mode** (physics simulation). This phase focuses on the **"Stage Builder"** experience, setting the stage for later phases that add character actions and story logic.
The stage is designed to be **extendable** so that future phases (Action Queue, Triggers, Save/Load) can plug in without major rewrites.
---
## 2. Scope
### 2.1. Whats Included
- **Mode Management:** Toggle between `EDIT` (placement/transform) and `PLAY` (physics simulation).
- **Object Spawning:** Palette buttons for placing terrain blocks (Ground, Ramp, Step), dynamic props (Crate, Ball), and Stickman rigs.
- **Selection & Hover:** Click to select, hover to highlight, and **box selection** for mass operations.
- **Transform Gizmos:** Translate and rotate handles for selected objects (visible only in Edit Mode).
- **Deletion:** Delete selected objects via the Delete/Backspace key.
- **Camera:** Pan (middlemouse) and zoom (mouse wheel) with configurable zoom limits.
- **Status Bar:** Shows current mode, object count, and selection info.
- **Signals:** Core events (`mode_changed`, `object_placed`, `object_selected`, `object_deleted`) for future extension.
### 2.2. Whats NOT Included (Future Phases)
- **Stickman navigation / pathfinding** (Phase 3 — Action Queue)
- **Speech bubbles / dialogue** (Phase 3)
- **Area triggers / sensors** (Phase 4)
- **Scene save/load** (Phase 5)
- **Graphical UI polish** (big toggle buttons, iconbased palette — Phase 5)
- **IK foot placement / slope alignment** (postPhase 3)
**Important:** Ramps and stairs can be **placed** and physics (props/ragdolls) will slide on them, but stickmen will **not** autonomously walk up/down them until Phase 3.
---
## 3. Architecture
### 3.1. Scene Hierarchy
res://scenes/sandbox_stage.tscn
├── SandboxStage (Node2D) ← Root script (class_name SandboxStage)
│ │
│ ├── World (Node2D) ← Parent container for ALL spawned objects
│ │ (TerrainBlocks, PropBlocks, StickmanRigs)
│ │
│ ├── GizmoLayer (Node2D) ← Draws selection outlines + transform handles
│ │ (visible only in EDIT mode)
│ │
│ ├── Camera2D ← Pan/zoom camera
│ │
│ ├── UI (CanvasLayer) ← Overlay UI
│ │ ├── TopBar (HBoxContainer)
│ │ │ ├── ModeToggle (Button) ← "Edit" ↔ "Play" (basic text)
│ │ │ ├── SpawnPalette (HBoxContainer)
│ │ │ │ ├── "Ground" (Button)
│ │ │ │ ├── "Ramp" (Button)
│ │ │ │ ├── "Step" (Button)
│ │ │ │ ├── "Crate" (Button)
│ │ │ │ ├── "Ball" (Button)
│ │ │ │ └── "Stickman" (Button)
│ │ │ └── StatusBar (Label) ← "Mode: EDIT | Objects: 12 | Selected: Crate"
│ │ └── (Future: Action Timeline, etc.)
│ │
│ └── Spawner (Node) ← Container for spawner logic (StageSpawner)
text
### 3.2. Scripts (Class Names)
| Script | Purpose |
|--------|---------|
| `sandbox_stage.gd` (`class_name SandboxStage`) | Root controller. Manages state, selection, mode, signals. |
| `stage_spawner.gd` (`class_name StageSpawner`, `extends RefCounted`) | Registry of spawnable types + factory methods. Reuses `TerrainUtils`, `PropUtils`, `StickmanFactory`. |
| `stage_selection.gd` (`class_name StageSelection`, `extends RefCounted`) | Handles hover, click selection, box selection, and selection signals. |
| `stage_gizmos.gd` (`class_name StageGizmos`, `extends Node2D`) | Draws and handles translate/rotate gizmos. |
### 3.3. State Management
- `enum StageMode { EDIT, PLAY }`
- `var current_mode: StageMode` (managed by root script)
- `var selected_objects: Array[Node2D]` (primary selection is index 0 for gizmos)
- **No global singleton** — all state is local to the `SandboxStage` instance, making it selfcontained and testable.
### 3.4. Signals
| Signal | Payload | Emitted When |
|--------|---------|--------------|
| `mode_changed(mode: int)` | `StageMode` enum | Mode toggle. |
| `object_placed(node: Node2D)` | Reference to placed object | After successful spawn. |
| `object_selected(nodes: Array[Node2D])` | Array of selected nodes | Selection changes. |
| `object_deselected()` | (none) | Selection cleared. |
| `object_deleted(nodes: Array[Node2D])` | Array of deleted nodes | After deletion. |
---
## 4. Key Features & Design Notes
### 4.1. Mode Management
- Toggle between **EDIT** (build) and **PLAY** (simulate).
- In `EDIT`: physics is frozen (`RigidBody2D.freeze = true`), gizmos visible, selection active.
- In `PLAY`: physics unfrozen, gizmos hidden, selection disabled.
### 4.2. Object Spawning
- Uses a **registry dictionary** in `StageSpawner` — adding a new object type is as simple as appending an entry (no hardcoded `match` statements).
- **Placement mode:** Click a palette button → next click on the `World` spawns the object.
- Repeated placement stays active until user presses **Escape** or clicks a different palette button.
- Spawn position uses the mouse world position (projected from the camera).
### 4.3. Selection (EDIT Mode Only)
- **Hover:** Subtle highlight/outline on the object under the mouse.
- **Single click:** Selects an object (deselects previous).
- **Box selection:** Click + drag on empty space draws a rectangle. Release selects all objects inside.
- Hold **Shift** to add to current selection instead of replacing.
- **Primary selection:** The first selected object (or the one clicked last) receives the transform gizmos.
### 4.4. Transform Gizmos (Primary Selection Only)
- **Translate:** Cross/directional handle — drag to move the object in world space.
- **Rotate:** Circular handle — drag to rotate the object around its center.
- Gizmos are implemented as `Area2D` nodes so they intercept mouse events (prevents accidental deselection or spawning).
- Gizmos are completely hidden in `PLAY` mode.
### 4.5. Deletion
- Press **Delete** or **Backspace** key to remove all currently selected objects.
- Deletion emits `object_deleted` with the list of removed nodes.
- Objects are `queue_free()`d — no orphaned nodes.
### 4.6. Camera
- **Pan:** Middlemouse drag.
- **Zoom:** Mouse wheel.
- Zoom min/max are `@export var` constants (`min_zoom = 0.1`, `max_zoom = 6.0`) in the root script, so they can be easily adjusted later.
- Camera persists across mode toggles.
### 4.7. Status Bar
- Shows: current mode, total object count, and selected object name/count.
- Updates in real time on selection, spawning, deletion, and mode change.
### 4.8. UI Polish (Future Phase 5)
- The Phase 2 UI uses **functional text buttons**.
- In Phase 5, these will be replaced with:
- A large, graphical **Mode Toggle** (slider/switch style).
- An iconbased **Spawn Palette** with draggable cards (draganddrop onto the stage).
---
## 5. Acceptance Criteria
### A. Mode Management
| # | Criterion | How to Test |
|---|-----------|-------------|
| A1 | A **Mode Toggle Button** switches between `EDIT` and `PLAY`. | Click; label changes. |
| A2 | In `EDIT` mode, **all RigidBody2D props are frozen**. | Place a PropBlock → PLAY → falls. Switch to EDIT → freezes. |
| A3 | In `PLAY` mode, **gizmos are hidden** and **selection is disabled**. | Select object in EDIT → PLAY → no selection box; clicks do nothing. |
| A4 | `mode_changed` signal is emitted on every toggle. | Connect a test print. |
---
### B. Object Spawning
| # | Criterion | How to Test |
|---|-----------|-------------|
| B1 | Spawn Palette contains: **Ground**, **Ramp**, **Step**, **Crate**, **Ball**, **Stickman**. | UI shows all six. |
| B2 | Clicking a palette button **enters placement mode**. | Click "Crate" → click world → crate appears. |
| B3 | **Repeated placement** stays active until Esc or different button. | Click "Crate" → click twice → two crates. |
| B4 | Uses existing factories: `TerrainUtils`, `PropUtils`, `StickmanFactory`. | Inspect spawned object properties. |
| B5 | `object_placed` signal is emitted. | Connect a test print. |
---
### C. Selection (Hover, Click, BoxSelect) — EDIT Only
| # | Criterion | How to Test |
|---|-----------|-------------|
| C1 | **Hover** highlights the object under the mouse. | Hover over a crate → it glows. |
| C2 | **Single Click** selects an object (deselects previous). | Click a crate → selection box appears. |
| C3 | **Deselect** by clicking empty space. | Click empty space → selection disappears. |
| C4 | Only **one primary selection** for gizmos. | Select crate → click ball → only ball has gizmos. |
| C5 | **Box Selection:** Click + drag draws a rectangle; release selects all objects inside. | Drag box around three crates → all selected. |
| C6 | **Shift+Box** adds to selection (does not replace). | Select A (click) → Shift+Box-select B & C → A, B, C selected. |
| C7 | `object_selected` and `object_deselected` signals are emitted. | Connect and verify. |
| C8 | **StatusBar** updates with selection info. | Box-select three crates → status shows "Selected: 3 objects". |
---
### D. Transform Gizmos (EDIT Only — Primary Selection)
| # | Criterion | How to Test |
|---|-----------|-------------|
| D1 | **Translate handle** appears around the selected object. | Select a crate → see move handle. |
| D2 | Dragging translate handle moves the object smoothly. | Drag → crate follows mouse. |
| D3 | **Rotate handle** appears around the selected object. | Select a crate → see rotation ring. |
| D4 | Dragging rotate handle rotates the object around its center. | Drag ring → crate spins. |
| D5 | Gizmos **do not appear** in PLAY mode. | Switch to PLAY → gizmos vanish. |
| D6 | Gizmos are **hittestable** (clicking them does NOT deselect or spawn). | Click move handle → object stays selected. |
---
### E. Deletion (EDIT Only)
| # | Criterion | How to Test |
|---|-----------|-------------|
| E1 | Pressing **Delete** or **Backspace** removes selected object(s). | Select a crate → press Delete → gone. |
| E2 | **Multiple deletion** works with box selection. | Box-select three crates → Delete → all gone. |
| E3 | `object_deleted` signal is emitted (array of nodes). | Connect and verify. |
| E4 | No orphaned nodes remain (`queue_free()` called). | Check `World` child count. |
---
### F. Camera & Viewport
| # | Criterion | How to Test |
|---|-----------|-------------|
| F1 | **Pan:** Middlemouse drag pans the view. | Drag → camera moves. |
| F2 | **Zoom:** Mouse wheel zooms in/out within `min_zoom`/`max_zoom` (exported). | Scroll within bounds. Edit constants → bounds update. |
| F3 | Camera does **not** reset on mode toggle. | Zoom/pan → toggle modes → view persists. |
---
### G. Status Bar
| # | Criterion | How to Test |
|---|-----------|-------------|
| G1 | StatusBar displays **Mode**, **Object Count**, and **Selection Info**. | Place 5 objects → "Objects: 5". Select one → "Selected: Crate". |
| G2 | StatusBar updates in real time. | Click, spawn, delete → label updates instantly. |
---
### H. Performance & Stability
| # | Criterion | How to Test |
|---|-----------|-------------|
| H1 | Spawning 50+ objects does not drop frames. | Click "Crate" 50 times → observe FPS. |
| H2 | Rapid toggling between EDIT and PLAY does not crash. | Toggle quickly → no errors. |
| H3 | No `push_warning` or errors in the console during normal use. | Monitor Output panel. |
---
### I. Extendability (Design)
| # | Criterion | Evidence |
|---|-----------|----------|
| I1 | Adding a new spawnable type requires **no modification** to `SandboxStage` — only a registry entry. | Code review: `StageSpawner` uses a Dictionary, not `match`. |
| I2 | `World` can hold **any** `Node2D`derived object. | Place a custom node → works. |
| I3 | Gizmos use `global_position`/`global_rotation` → work for any object. | Select a TerrainBlock → gizmos appear and move it. |
| I4 | Signals provide hooks for future systems (Save/Load, Action Queue, Triggers). | Code review: signals exist for all major events. |
| I5 | UI palette is built from data, not hardcoded buttons. | Adding a button = appending to an array. |
---
## 6. Future Considerations
### 6.1. Ramp / Stair Dynamics
| Feature | Phase 2 | Phase 3+ |
|---------|---------|----------|
| Place ramp/step terrain | ✅ | ✅ |
| Props roll/fall on ramps | ✅ (physics) | ✅ |
| Ragdoll tumbles on ramps | ✅ (physics) | ✅ |
| Stickman **walks up** ramp | ❌ | ✅ (NavigationAgent2D) |
| Stickman **navigates** stairs | ❌ | ✅ (NavigationAgent2D + IK) |
### 6.2. UI Polish (Phase 5)
- Replace the textbased Mode Toggle with a **large, kidfriendly graphical switch/slider**.
- Replace text palette buttons with **iconbased draggable cards** (draganddrop onto the stage).
- Add tooltips and animations for feedback.
### 6.3. Box Selection Enhancements (PostPhase 2)
- Option to **invert** selection (select all except those inside box).
- **Lock** selected objects (prevent accidental moves).
---
## 7. Summary of Deliverables
| File | Purpose |
|------|---------|
| `res://scenes/sandbox_stage.tscn` | The main stage scene. |
| `res://scripts/sandbox_stage.gd` | Root controller (`class_name SandboxStage`). |
| `res://scripts/stage_spawner.gd` | Spawn registry + factory (`class_name StageSpawner`). |
| `res://scripts/stage_selection.gd` | Selection logic (`class_name StageSelection`). |
| `res://scripts/stage_gizmos.gd` | Gizmo rendering + interaction (`class_name StageGizmos`). |
---
## 8. Acceptance SignOff Checklist
- [ ] Mode toggle switches between EDIT and PLAY.
- [ ] Physics freezes/unfreezes correctly.
- [ ] All six spawnable types can be placed.
- [ ] Repeated placement works.
- [ ] Hover highlights objects.
- [ ] Singleclick selection works.
- [ ] Box selection works (with Shift addtoselection).
- [ ] Translate gizmo works.
- [ ] Rotate gizmo works.
- [ ] Delete key removes selected objects.
- [ ] Camera pans and zooms within configurable limits.
- [ ] StatusBar updates correctly.
- [ ] All signals are emitted.
- [ ] No errors/warnings in console.
- [ ] Performance is acceptable (50+ objects).
---
*End of Phase 2 Plan*