feat: Add project roadmap and initial specifications for dynamic vector props and terrain system
- Created ROADMAP.md outlining core features, implementation phases, and detailed breakdown for the Stickman Sandbox Builder project. - Introduced plans for dynamic vector props with `PropBlock` specification, including reusable components and utility functions for prop creation. - Developed a vector terrain system plan detailing the reusable `TerrainBlock` component and associated utility functions for geometry handling. - Implemented `PhysicsTestHarness` scene for testing physics interactions with dynamic props and terrain. - Added scripts for `PropBlock`, `PropUtils`, `TerrainBlock`, and `TerrainUtils` to support dynamic prop creation and terrain management.
This commit is contained in:
@@ -365,6 +365,63 @@ assembled in a "Whole Stickman" preview that supports translation, rotation, and
|
||||
persistence to disk. The "Facing" menu and all animation controls are **hidden until an .stk is
|
||||
loaded** (`_set_rig_controls_visible(false)` at the end of `_build_ui()` and in
|
||||
`_free_current_rig()`; shown on successful spawn in `_load_and_spawn()`).
|
||||
- `scripts/terrain_block.gd` — `class_name TerrainBlock`, `extends StaticBody2D`; a **reusable
|
||||
vector terrain component** (Vector Terrain System, **not used by the editor**). Builds its three
|
||||
children in code: `Polygon2D` (interior fill, `fill_color`), `Line2D` (crisp vector border,
|
||||
`outline_color`/`outline_width`, auto-closed loop by appending the first vertex to the end,
|
||||
`LINE_JOINT_ROUND` + round caps), and `CollisionPolygon2D` (`BUILD_SOLIDS` solid decomposition —
|
||||
supports concave blocks). Exported properties: `polygon_points: PackedVector2Array`,
|
||||
`fill_color`, `outline_color`, `outline_width`; a unified setter pushes vertex changes to all
|
||||
three children live (no manual rebuilds).
|
||||
- `scripts/terrain_utils.gd` — `class_name TerrainUtils`, `extends RefCounted`; static utility
|
||||
(**not used by the editor**):
|
||||
- `sanitize_points(points: PackedVector2Array, grid_size: float = 16.0) -> PackedVector2Array` —
|
||||
sanitization pipeline in order: grid snap → redundancy removal via a **local
|
||||
`_simplify_polyline()`** (Godot 4.7 has **no `Geometry2D.simplify_polyline()`**; the local
|
||||
version drops consecutive duplicates, a closing duplicate when `last == first`, and collinear
|
||||
vertices) → clockwise enforcement via `Geometry2D.is_polygon_clockwise()` (reverses if false,
|
||||
guaranteeing clockwise output).
|
||||
- `spawn_block(...)` — factory that sanitizes raw input vectors (`sanitize_points`), creates a
|
||||
`TerrainBlock`, applies the cleaned points, and adds it to the target container.
|
||||
- `scripts/prop_block.gd` — `class_name PropBlock`, `extends RigidBody2D`; a **reusable
|
||||
dynamic vector prop component** (Dynamic Vector Props, **not used by the editor**), `@tool`.
|
||||
Builds its children in code: `Polygon2D` (interior fill, `fill_color`), `Line2D` (crisp
|
||||
vector outline, `outline_color`/`outline_width`, auto-closed loop by appending the first
|
||||
vertex, `LINE_JOINT_ROUND` + `LINE_CAP_ROUND`), and a collision node — `CollisionPolygon2D`
|
||||
(`BUILD_SOLIDS`) in `POLYGON` mode, or `CollisionShape2D` + `CircleShape2D` (48-segment
|
||||
radial loop, `CIRCLE_SEGMENTS = 48`) in `CIRCLE` mode, toggled via `shape_type`. Exported
|
||||
properties: `shape_type` (`@export_enum("Polygon","Circle")`), `polygon_points:
|
||||
PackedVector2Array`, `radius: float`, `fill_color`, `outline_color`, `outline_width`, and
|
||||
`material_preset` (`@export_enum("None","Wood","Rubber","Cardboard","Metal")`). Presets set
|
||||
`mass` + `physics_material_override` via static `mass_for()`/`physics_material()`/`tint_for()`
|
||||
(Wood: mass 3.0, friction 0.6, bounce 0.1; Rubber: mass 0.5, friction 0.9, bounce 0.85;
|
||||
Cardboard: mass 0.4, friction 0.3, bounce 0.05; Metal: mass 8.0, friction 0.9, bounce 0.0;
|
||||
None: mass 1.0, friction 0.5, bounce 0.05) and recolor fill/outline for non-`NONE`. Unified
|
||||
live-update setters push geometry/color changes to all children (null-guarded for `@tool`
|
||||
editor safety); `_apply_shape()` enables exactly one collision node.
|
||||
- `scripts/prop_utils.gd` — `class_name PropUtils`, `extends RefCounted`; static factory
|
||||
(**not used by the editor**):
|
||||
- `create_box(size := Vector2(48,48))` / `create_ball(radius := 24.0)` /
|
||||
`create_plank(length := 160.0, thickness := 16.0)` / `create_triangle(base := 56.0, height
|
||||
:= 48.0)` — primitive generators returning shape-payload dictionaries with default
|
||||
dimensions + color themes (wood/rubber/metal/cardboard).
|
||||
- `spawn_prop(container, position, shape_payload, material_preset := WOOD, initial_velocity
|
||||
:= Vector2.ZERO) -> PropBlock` — instantiates a `PropBlock`, applies the payload
|
||||
(`shape_type` + geometry + colors via `_apply_shape_payload()`, sanitizing polygon points
|
||||
through `TerrainUtils.sanitize_points()`), sets `linear_velocity` after `add_child` (only
|
||||
when non-zero), and returns the spawned prop.
|
||||
- `scripts/physics_test_harness.gd` — `class_name PhysicsTestHarness`, `extends Node2D`; standalone
|
||||
staging scene root (Vector Terrain System / Dynamic Vector Props, **not wired into the editor**;
|
||||
run via **F6** on `res://scenes/physics_test_harness.tscn`). Builds flat ground, angled ramps, and stepped
|
||||
`TerrainBlock` instances via `TerrainUtils`, instantiates `res://master_rig.tscn` standing on the
|
||||
flat ground, and handles camera input. **Keys 1/2/3** spawn dynamic props above the angled ramp
|
||||
via `PropUtils.spawn_prop()` (`PROP_SPAWN_POSITION = (300, -300)`): **1** Wood Crate
|
||||
(`create_box()`, `WOOD`, velocity `(60,0)`), **2** Bouncy Ball (`create_ball()`, `RUBBER`,
|
||||
`(-80,0)`), **3** Heavy Plank (`create_plank()`, `METAL`, `(30,-40)`). Adds a **best-effort
|
||||
`StaticBody2D` collision proxy** (`RigCollisionProxy`, `_add_rig_collision_proxy()`) since the
|
||||
rig has **no physics bodies of its own** — a 240×1000 px `RectangleShape2D` centered at `(0,-500)`
|
||||
(`RIG_PROXY_SIZE`/`RIG_PROXY_CENTER`) matching the standing figure's world bounds, so props
|
||||
bounce/rest against it; the proxy is a code-only stand-in, not part of the rig.
|
||||
- Scenes:
|
||||
- `scenes/stickman_editor.tscn` — main editor layout; unique-name nodes (`%Prefix`) used
|
||||
for typed `@onready` access: `%MenuBar`, `%StickmanNameEdit`, `%LeftColumn`,
|
||||
@@ -382,6 +439,13 @@ assembled in a "Whole Stickman" preview that supports translation, rotation, and
|
||||
loaded-filename status label;
|
||||
`SubViewport` world with an enabled `Camera2D` (middle-mouse pan, wheel zoom, recenter on
|
||||
spawn); interactive limb IK via `SkeletonModificationStack2D` TwoBoneIK.
|
||||
- `scenes/physics_test_harness.tscn` — **standalone staging scene** (Vector Terrain System /
|
||||
Dynamic Vector Props, not wired into the editor; run via **F6**). Backed by
|
||||
`scripts/physics_test_harness.gd`. Root `Node2D` + script, `Camera2D` at position `(0, -400)`
|
||||
zoom `0.5`, empty Environment container. Wheel-zoom scales between `0.25x` and `3.0x`
|
||||
(resolution independence / vector outline thickness); middle-drag pans. Keys **1/2/3** spawn
|
||||
dynamic props (`PropUtils`) above the angled ramp; a best-effort `StaticBody2D` rig collision
|
||||
proxy provides a surface for props to bounce/rest against.
|
||||
|
||||
### Body-part data model
|
||||
- 10 internal part keys (ordered): `head`, `torso`, `left_upper_arm`, `left_lower_arm`,
|
||||
|
||||
Reference in New Issue
Block a user