- Implement test for popup anchor behavior in rule-builder menus to ensure consistent anchor positioning during menu transitions. - Create tests for stage logic, including mode transitions, toolbar visibility, and status bar updates. - Add terrain drag-painting tests to verify correct block placement behavior and conflict handling. - Introduce walk waypoint tests to check for arrival conditions and position stability after navigation.
331 lines
18 KiB
Markdown
331 lines
18 KiB
Markdown
# Phase 3b: Asset Library — Stickman + Prop Selector Grids
|
||
|
||
## 1. Overview
|
||
|
||
Phase 3b adds a **visual asset library** to the Sandbox Stage Builder. Currently, placing a stickman always spawns the hard-coded `test.stk` figure, and props are limited to the palette buttons (Crate, Ball, Plank). This phase replaces those limitations with **visual selector grids** that let the user choose from all available assets.
|
||
|
||
## 2. Core Concept
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ ASSET LIBRARY WORKFLOW │
|
||
├─────────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ EDIT MODE │
|
||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||
│ │ User clicks "Stickman" palette button │ │
|
||
│ │ ↓ │ │
|
||
│ │ Stickman Selector Grid opens (popup overlay) │ │
|
||
│ │ ┌─────────────────────────────────────────────────────┐ │ │
|
||
│ │ │ Choose Your Stickman [×] Close │ │ │
|
||
│ │ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │
|
||
│ │ │ │ 📷 │ │ 📷 │ │ 📷 │ │ 📷 │ │ │ │
|
||
│ │ │ │ Bob │ │ Sally│ │ John │ │ Joe │ │ │ │
|
||
│ │ │ └──────┘ └──────┘ └──────┘ └──────┘ │ │ │
|
||
│ │ │ ┌──────┐ ┌──────┐ │ │ │
|
||
│ │ │ │ 📷 │ │ 📷 │ │ │ │
|
||
│ │ │ │ Sue │ │ Tom │ │ │ │
|
||
│ │ │ └──────┘ └──────┘ │ │ │
|
||
│ │ │ [Prev] Page 1/2 [Next] [Browse...] [Refresh] │ │ │
|
||
│ │ └─────────────────────────────────────────────────────┘ │ │
|
||
│ │ ↓ │ │
|
||
│ │ User clicks a cell → grid closes │ │
|
||
│ │ ↓ │ │
|
||
│ │ Click the stage → spawns the selected stickman │ │
|
||
│ └─────────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────────────┘
|
||
|
||
```
|
||
|
||
## 3. What's Being Replaced
|
||
|
||
| Before | After |
|
||
| ------------------------------------------- | ------------------------------------------------- |
|
||
| "Stickman" palette button spawns `test.stk` | "Stickman" palette button opens the selector grid |
|
||
| Separate "Crate", "Ball", "Plank" buttons | Single "Prop" button opens the prop selector grid |
|
||
| No visual preview | Thumbnail preview in each grid cell |
|
||
| No choice | Choose from all available assets |
|
||
| Hard-coded file path | Dynamic library from scanned files |
|
||
|
||
## 4. Feature Breakdown
|
||
|
||
### 4.1. Stickman Selector Grid
|
||
|
||
| Feature | Description |
|
||
| --------------- | ------------------------------------------------------------------------ |
|
||
| **Source** | Scans `res://stickmen/` for `.stk` files |
|
||
| **Thumbnails** | Renders each stickman to a texture (cached to `user://thumbnails/`) |
|
||
| **Display** | Grid cells: thumbnail + `stickman_name` (or filename if no name) |
|
||
| **Pagination** | 12 items per page (3 rows × 4 columns) with Prev/Next buttons |
|
||
| **Selection** | Click a cell → selected stickman is cached as the default |
|
||
| **Browse** | "Browse..." button opens a `FileDialog` to select a `.stk` from anywhere |
|
||
| **Refresh** | "Refresh" button rescans the `stickmen/` folder |
|
||
| **Empty state** | "No stickmen found! Create one in the editor first." |
|
||
|
||
### 4.2. Prop Selector Grid
|
||
|
||
| Feature | Description |
|
||
| -------------- | ------------------------------------------------------ |
|
||
| **Source** | Built-in prop templates (Crate, Ball, Plank, Triangle) |
|
||
| **Thumbnails** | Renders each prop with its material/color |
|
||
| **Display** | Grid cells: thumbnail + prop name + material badge |
|
||
| **Pagination** | 12 items per page with Prev/Next buttons |
|
||
| **Selection** | Click a cell → selected prop is cached as the default |
|
||
| **Future** | Custom props (`.prp` files) will appear here |
|
||
|
||
### 4.3. Single Palette Buttons
|
||
|
||
| Before | After |
|
||
| ----------------------- | ---------------------------- |
|
||
| "Stickman" (hard-coded) | "Stickman" (opens grid) |
|
||
| "Crate" (hard-coded) | Removed |
|
||
| "Ball" (hard-coded) | Removed |
|
||
| "Plank" (hard-coded) | Removed |
|
||
| (None) | **"Prop"** (opens prop grid) |
|
||
|
||
## 5. Visual Design
|
||
|
||
### 5.1. Grid Cell Layout
|
||
|
||
```
|
||
┌─────────────────────────────────────┐
|
||
│ ┌─────────────────────────────┐ │
|
||
│ │ │ │
|
||
│ │ [THUMBNAIL] │ │ ← 150×150 px preview
|
||
│ │ │ │
|
||
│ └─────────────────────────────┘ │
|
||
│ │
|
||
│ ┌─────────────────────────────┐ │
|
||
│ │ Bob │ │ ← Name (bold, centered)
|
||
│ │ 🪵 Wood │ │ ← Material badge (props only)
|
||
│ └─────────────────────────────┘ │
|
||
└─────────────────────────────────────┘
|
||
|
||
```
|
||
|
||
**Cell size:** ~160×220 px (thumbnail 150×150, name area 60px)
|
||
|
||
### 5.2. Grid Popup Layout
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────────┐
|
||
│ Choose Your Stickman [×] Close │
|
||
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
|
||
│ │ 📷 │ │ 📷 │ │ 📷 │ │ 📷 │ │
|
||
│ │ Bob │ │Sally │ │ John │ │ Joe │ │
|
||
│ └──────┘ └──────┘ └──────┘ └──────┘ │
|
||
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
|
||
│ │ 📷 │ │ 📷 │ │ 📷 │ │ 📷 │ │
|
||
│ │ Sue │ │ Tom │ │Alex │ │Jess │ │
|
||
│ └──────┘ └──────┘ └──────┘ └──────┘ │
|
||
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
|
||
│ │ 📷 │ │ 📷 │ │ 📷 │ │ 📷 │ │
|
||
│ │Sam │ │Ella │ │Max │ │Mia │ │
|
||
│ └──────┘ └──────┘ └──────┘ └──────┘ │
|
||
│ │
|
||
│ [← Prev] Page 1/3 [Next →] [Browse...] [Refresh] │
|
||
│ │
|
||
│ Selected: Bob │
|
||
└─────────────────────────────────────────────────────────────────────────┘
|
||
|
||
```
|
||
|
||
### 5.3. Visual States
|
||
|
||
| State | Appearance |
|
||
| --------------------- | ------------------------------------- |
|
||
| **Default cell** | Light background, subtle border |
|
||
| **Hover** | Border highlight, slight scale (1.05) |
|
||
| **Selected** | Blue/cyan border, checkmark overlay |
|
||
| **Loading thumbnail** | Spinner or placeholder icon |
|
||
| **Empty cell** | "No preview available" placeholder |
|
||
|
||
## 6. Data Flow
|
||
|
||
### 6.1. Stickman Library Indexer
|
||
|
||
```gdscript
|
||
# stickman_library.gd (NEW)
|
||
class_name StickmanLibrary
|
||
extends RefCounted
|
||
|
||
# Scans res://stickmen/*.stk
|
||
# Returns Array[Dictionary] with:
|
||
# {
|
||
# "path": "res://stickmen/bob.stk",
|
||
# "name": "Bob",
|
||
# "thumbnail": Texture2D,
|
||
# "data": Dictionary # parsed .stk data (optional)
|
||
# }
|
||
```
|
||
|
||
### 6.2. Prop Library Registry
|
||
|
||
```gdscript
|
||
# prop_library.gd (NEW)
|
||
class_name PropLibrary
|
||
extends RefCounted
|
||
|
||
# Built-in prop templates:
|
||
# - Crate (Wood)
|
||
# - Ball (Rubber)
|
||
# - Plank (Metal)
|
||
# - Triangle (Cardboard)
|
||
|
||
# Returns Array[Dictionary] with:
|
||
# {
|
||
# "id": "crate",
|
||
# "name": "Crate",
|
||
# "factory": Callable (returns shape payload),
|
||
# "material_preset": PropBlock.MaterialPreset.WOOD,
|
||
# "thumbnail": Texture2D
|
||
# }
|
||
```
|
||
|
||
### 6.3. Thumbnail Generation
|
||
|
||
#### Stickman Thumbnails:
|
||
|
||
- Parse the .stk file to get body_parts and part_order.
|
||
- Create a temporary SubViewport (200×200 px).
|
||
- Render the stickman using a simplified version of WholeStickmanPreview logic.
|
||
- Capture the viewport as a Texture2D.
|
||
- Cache to user://thumbnails/stickmen/<hash>.png.
|
||
|
||
#### Prop Thumbnails:
|
||
|
||
- Instantiate a temporary PropBlock in a SubViewport.
|
||
- Apply the material preset and geometry.
|
||
- Capture the viewport as a Texture2D.
|
||
- Cache to user://thumbnails/props/<id>.png.
|
||
|
||
#### Caching Strategy:
|
||
|
||
- Thumbnails are generated once per file and cached to disk.
|
||
- On subsequent runs, load the cached thumbnail if it exists and the source file hasn't been modified.
|
||
- If the source file is modified, regenerate the thumbnail.
|
||
|
||
## 7. UI Integration
|
||
|
||
### 7.1. Palette Button Changes
|
||
|
||
#### Button Behavior
|
||
|
||
- "Stickman" Opens the Stickman Selector Grid
|
||
- "Prop" Opens the Prop Selector Grid
|
||
|
||
### 7.2. Selected Asset Persistence
|
||
|
||
- The last selected stickman is stored in StageSpawner.selected_stickman_path.
|
||
- The last selected prop is stored in StageSpawner.selected_prop_id.
|
||
- These persist across mode toggles and scene reloads (in memory only — no disk save in Phase 3b).
|
||
|
||
### 7.3. Placement Flow
|
||
|
||
```
|
||
User clicks "Stickman" palette button
|
||
↓
|
||
Stickman Selector Grid opens
|
||
↓
|
||
User clicks a stickman cell
|
||
↓
|
||
Grid closes → selected stickman is cached
|
||
↓
|
||
Stage enters placement mode (ghost appears)
|
||
↓
|
||
User clicks the stage → spawns the selected stickman
|
||
```
|
||
|
||
## 8. File Structure
|
||
|
||
| File | Purpose |
|
||
| ---------------------------------------------- | ------------------------------------------------------ |
|
||
| res://scripts/stickman_library.gd | Scans .stk files, manages thumbnails |
|
||
| res://scripts/prop_library.gd | Registry of prop templates |
|
||
| res://scripts/asset_selector.gd | Grid UI controller (shared between stickmen and props) |
|
||
| res://scenes/asset_selector.tscn | Grid popup scene |
|
||
| res://scripts/thumbnails/stickman_thumbnail.gd | Renders stickman to texture |
|
||
| res://scripts/thumbnails/prop_thumbnail.gd | Renders prop to texture |
|
||
| res://user://thumbnails/ | Cached thumbnails (generated at runtime) |
|
||
|
||
## 9. Acceptance Criteria
|
||
|
||
### 9.1. Stickman Selector Grid
|
||
|
||
- [ ] Clicking "Stickman" palette button opens the grid.
|
||
- [ ] Grid displays all .stk files in res://stickmen/.
|
||
- [ ] Each cell shows a thumbnail preview of the stickman.
|
||
- [ ] Each cell shows the stickman's stickman_name (or filename if no name).
|
||
- [ ] Clicking a cell selects that stickman and closes the grid.
|
||
- [ ] The selected stickman persists for future placements.
|
||
- [ ] Pagination works when more than 12 stickmen exist.
|
||
- [ ] "Browse..." button opens a FileDialog to select any .stk.
|
||
- [ ] "Refresh" button rescans the stickmen/ folder.
|
||
- [ ] If no .stk files exist, shows "No stickmen found! Create one in the editor first."
|
||
- [ ] If only one .stk exists, the grid is skipped and the stickman is selected directly.
|
||
- [ ] Thumbnails are cached to user://thumbnails/ and reused.
|
||
- [ ] Modified .stk files regenerate their thumbnails.
|
||
|
||
### 9.2. Prop Selector Grid
|
||
|
||
- [ ] Clicking "Prop" palette button opens the grid.
|
||
- [ ] Grid displays all built-in prop templates.
|
||
- [ ] Each cell shows a thumbnail preview of the prop.
|
||
- [ ] Each cell shows the prop name and material badge.
|
||
- [ ] Clicking a cell selects that prop and closes the grid.
|
||
- [ ] The selected prop persists for future placements.
|
||
- [ ] Pagination works when more than 12 props exist.
|
||
- [ ] Thumbnails are cached to user://thumbnails/props/.
|
||
|
||
### 9.3. Palette Integration
|
||
|
||
- [ ] The old "Crate", "Ball", and "Plank" buttons are removed.
|
||
- [ ] A single "Prop" button replaces them.
|
||
- [ ] The "Stickman" button now opens the grid (instead of spawning test.stk).
|
||
|
||
### 9.4. Performance
|
||
|
||
- [ ] Scanning 50+ .stk files does not stall the UI.
|
||
- [ ] Thumbnail generation is non-blocking (or uses a loading state).
|
||
- [ ] Grid opens and paginates smoothly.
|
||
|
||
## 10. Implementation Order
|
||
|
||
| Step | Task | Dependencies |
|
||
| ---- | ---------------------------------------------------------- | ------------ |
|
||
| 1 | Create stickman_library.gd (file scanner + entry model) | None |
|
||
| 2 | Create stickman_thumbnail.gd (renders stickman to texture) | Step 1 |
|
||
| 3 | Create asset_selector.gd / asset_selector.tscn (grid UI) | Steps 1-2 |
|
||
| 4 | Integrate selector with "Stickman" palette button | Step 3 |
|
||
| 5 | Create prop_library.gd (built-in prop registry) | None |
|
||
| 6 | Create prop_thumbnail.gd (renders prop to texture) | Step 5 |
|
||
| 7 | Integrate selector with "Prop" palette button | Steps 5-6 |
|
||
| 8 | Remove old "Crate", "Ball", "Plank" buttons | Step 7 |
|
||
| 9 | Implement thumbnail caching | Steps 2, 6 |
|
||
|
||
## 11. Edge Cases
|
||
|
||
| Edge Case | Handling |
|
||
| -------------------------- | --------------------------------------------------------- |
|
||
| No .stk files exist | Show "No stickmen found! Create one in the editor first." |
|
||
| Only one .stk exists | Skip the grid, select it directly |
|
||
| Thumbnail generation fails | Show placeholder icon + regenerate on next run |
|
||
| File is corrupted/invalid | Skip the file, log a warning |
|
||
| Stickman has no name | Use the filename (without .stk) |
|
||
| Many files (50+) | Pagination keeps the UI responsive |
|
||
| Grid size changes | Rebuild the grid layout on resize |
|
||
| Selected file is deleted | Reselect the first available file (or show empty state) |
|
||
|
||
## 12. Summary
|
||
|
||
| Before | After |
|
||
| --------------------------------- | ------------------------------------- |
|
||
| Hard-coded test.stk | Visual grid of all .stk files |
|
||
| Separate Crate/Ball/Plank buttons | Single "Prop" button with visual grid |
|
||
| No preview | Thumbnail preview in every cell |
|
||
| No choice | Choose from all available assets |
|
||
| No caching | Thumbnails cached to user:// |
|
||
|
||
This is the final piece connecting the Stickman Editor to the Sandbox Stage. Users can now create stickmen in the editor, save them as .stk, and pick them from the grid when placing actors on the stage.
|