- Introduced `PHASE_3a_CORE_DIRECTOR.md` detailing the core functionality for directors, including navigation, action queue, UI, waypoint visualization, and action execution. - Implemented `StageDirectorVisuals` for drawing stickman action queues in edit mode, including waypoints and action badges. - Created `SpeechBubble` class for displaying speech bubbles above stickmen, with customizable text and styling.
126 lines
18 KiB
Markdown
126 lines
18 KiB
Markdown
# Stickman Studio Bugs
|
||
|
||
## Stickman editor (Phase 5 Round 1)
|
||
|
||
### Snap to grid
|
||
|
||
1. The snap to grid menu item checkbox is still not populating with a check mark no matter how many times it is clicked.
|
||
|
||
### Shape copy and paste
|
||
|
||
1. The paste item in the context menu is only displaying if a shape is selected and the context menu is brought up. If I have an empty window OR if I don't have a shape selected in that window, 'paste' do not show up in the menu. It should show up as long as something is in the clipboard. Pasting into an empty window should be possible.
|
||
|
||
## Stickman editor (Phase 5 Round 2)
|
||
|
||
### Snap to grid
|
||
|
||
1. The snap to grid menu item checkbox is still not populating with a check mark. Should we come up with another solution for this?
|
||
|
||
### Object scaling after rotation
|
||
|
||
1. When I rotate an object to a different angle than the original (0) and the try to scale the object, the object returns back to 0 rotation and then begins to scale. The scale should happen while the object is rotated, and the object keeps that rotation while being scaled.
|
||
|
||
## Stickman editor (Phase 5 Round 3)
|
||
|
||
### Snap to grid
|
||
|
||
1. The snap to grid menu item checkbox is still not populating with a check mark. Let's change the implementation. _Propose an alternate solution_
|
||
|
||
### Pasting a shape
|
||
|
||
1. If an object has been scaled or rotated, and then the object's corresponding shape is deleted from a shape editor window - when a new shape is pasted, that shape takes on the scale, rotation, and position of the previous deleted shape or object had. When a shape is deleted, it's corrsponding object need to have it's position, rotation, and scale reset.
|
||
|
||
## Stickman editor (Phase 5 Round 4)
|
||
|
||
### Snap to grid when moving an object
|
||
|
||
1. Currently snapping to the grid snaps the mouse cursor to the grid, however, that can lead to object still not lining up properly. The object's bounding box should be what snaps to the grid. If a reference point is needed for snapping, use the object's upper left point of the bounding box.
|
||
|
||
## Stickman editor (Phase 6 Round 1)
|
||
|
||
### Seletion bounding box as well as rotation 'dot' should stay in front of all other objects.
|
||
|
||
If an object is selected, it's bounding box and rotation dot should be in front of all other objects.
|
||
|
||
### Touchpad panning is too slow
|
||
|
||
Panning using the touchpad does not moves too slowly. We need to increase the distance the panning moves through touchpad.
|
||
|
||
### Zooming in/out
|
||
|
||
When zooming in, we should use the mouse cursor as the zoom in point. If the cursor is in the window should determine where the zoom should happen.
|
||
|
||
## Stickman editor (Phase 7 Round 1)
|
||
|
||
### Silhouette starting position (or camera)
|
||
|
||
When starting the application, the silhouette stickman should be in the center of the view. Right now it starts on the left upper side of the view so the user has to pan over.
|
||
Either move the silhouette or the camera.
|
||
|
||
### Showing the pose guide
|
||
|
||
When clicking View->Show Pose Guide the guide toggles on and off, but the text never changes.
|
||
By default the pose is visible so the menu text should say "Hide Pose Guide"
|
||
When it is hidden it should say "Show Pose Guide"
|
||
|
||
### Guide visibility
|
||
|
||
The guide and joints should slightly 'ghost' in front of the objects so that the use can see how well the objects are aligned to the joints and limbs.
|
||
|
||
## Stickman editor (Phase 9 Round 1)
|
||
|
||
> FIXED — 2026 Round 1: implemented per docs/phase9_round1_bugfix_spec.md; anisotropic scaling, joint-based anchors, and node/driver transform neutralization; verified with a 37-assertion headless smoke test.
|
||
|
||
## Stickman editor (Phase 9 Round 2)
|
||
|
||
> FIXED — 2026 Round 2: implemented per docs/phase9_round2_bugfix_spec.md; restored the `RemoteTransform2D` driver rotations (`update_rotation = true` again) so mounted shapes follow their bones under IK flexing, switched the mount to the rig's hanging convention (joint anchor at the local origin, far end along +Y, per-family alignment rotation), auto-detected the drawn long axis so only that axis scales to the bone length (cross-axis thickness 1:1), zeroed the Head driver's local position so the chin lands on the neck joint, dropped `DEFAULT_LINE_WIDTH` to 2.0 (editor's outline width), and rewrote the harness bone overlay (true parent→child segments + leaf bones drawn to their IK targets). Verified with a 46-assertion headless smoke test.
|
||
|
||
1. **Lower-limb bones/joints invisible in "Show Bones".** The harness `_draw_bones()` drew parent-origin → bone-origin lines, so for lower bones the segment duplicated the upper segment and wrist/ankle joints (bone ends, not origins) were never drawn. Fixed: the overlay now draws a joint dot per bone, parent→child bone segments, and leaf bones out to their IK targets.
|
||
2. **IK only moves lower-arm shapes; upper-arm shapes static.** Round 1's `_neutralize_driver_rotations()` set `update_rotation = false` on the 10 `RemoteTransform2D` drivers, so `Body/*` visuals sat unrotated at fixed joints. Fixed: driver rotation neutralization removed — the drivers keep their defaults and the hanging-convention mount rotates every part to follow its bone in any pose.
|
||
3. **Torso top connects at the hip joint.** With driver rotation suppressed, the torso (top-center anchor) hung with its top at the hip-driven origin. Fixed: the torso now mounts with the hip end (bottom-center anchor, θ = π) at the origin, so the top lands at the neck.
|
||
4. **Head features wrong scale / too thick / nose not triangular.** `DEFAULT_LINE_WIDTH = 16.0` dominated small face features, and the Head driver's local position `(0.05, −72)` floated the head above the neck. Fixed: line width is 2.0 (editor's 2 px outline) and the Head driver position is zeroed so the mounted head's chin sits on the neck joint.
|
||
|
||
## Stickman editor (Phase 9 Round 3)
|
||
|
||
> FIXED — 2026 Round 3: implemented per docs/phase9_round3_bugfix_spec.md; one node per shape (closed → single Polygon2D, open → single Line2D), the part's preview rotation/scale composed into the mount before the hanging-convention fit (anchor/θ/bone-fit scale computed on the transformed geometry; ±180° flips swap the attachment to the drawn far end so flips render), and the harness head-leaf overlay drawn along the bone's own direction instead of to the LookAt aim point. Verified with a 26-assertion headless smoke test.
|
||
|
||
1. **"The head bone has another bone extending out of it."** The harness bone overlay drew the Head leaf bone as a segment to `IK_Targets/Head` — the `SkeletonModification2DLookAt` aim point 232 px above the neck — a long line sticking out of the head. Fixed: the Head leaf now draws a ~90 px segment along the bone's own direction (`Vector2(length, 0)` rotated by `bone_angle` then `global_rotation`), so the head bone runs neck→head-top. Limb leaf bones (LeftLowerArm, RightLowerArm, LeftLowerLeg, RightLowerLeg) still draw to their IK targets.
|
||
2. **Duplicate Polygon2D + Line2D pairs per closed shape.** Closed shapes mounted as a `Polygon2D` fill **plus** a `Line2D` outline, producing two nodes per shape (e.g. `@Polygon2D@165` + `@Line2D@166`). Fixed: one node per shape — closed shapes mount as a single `Polygon2D`, open shapes as a single `Line2D` (width 2).
|
||
3. **Part rotation not applied.** The adapter ignored the per-part `rotation` (degrees) the user applied in the Whole Stickman preview — a torso rotated 180° looked identical to an unrotated one. Fixed: the mount now applies the preview's exact transform `E(P) = C + R(rot)·S·(P − C)` (scale-then-rotate about the bbox center) **before** the hanging-convention mount, and the anchor/alignment θ/bone-fit scale are computed on the transformed geometry; rotations near ±180° (`|wrapf(rot)| > 0.75π`) additionally swap the attachment to the drawn far end so flips are visible (e.g. the 180° torso shows its drawn neck end at the hip joint).
|
||
4. **Part scale not applied / head too small.** The adapter ignored the per-part `scale`, so a head scaled ~2× in the editor (≈160 px face) rendered ~80×80 in the harness. Fixed: part scale composes into `E` (the head mounts upright with θ = 0, s = 1 but still applies the part scale via `E`, so the face mounts at ≈160 px and the chin sits on the neck joint); the bone-fit scale `s` is measured on the transformed extent, so user-scaled parts are not double-fitted.
|
||
|
||
### Fix Shape Mount Math & Point Scaling in StkRigAdapter.gd
|
||
|
||
Problem Summary:
|
||
When applying .stk v1.4 data to master*rig.tscn, shapes are severely distorted:Giant Polygon Explosions (Legs): Uniformly scaling points using scale_factor = bone_length / part_length on both $X$ and $Y$ multiplies the shape's thickness, turning thin leg segments into screen-filling blocks.Misaligned Joint Rotations (Head/Torso): Using (min + max) / 2 sets the pivot to the geometric center of the shape instead of its joint connection (e.g., neck base or hip joint).Required Fixes in StkRigAdapter.gd (or ActorFactory.gd):Please refactor \_mount_shapes() / point calculation logic using the following rules:1. Anisotropic Scaling (Primary Axis Only)Do not apply scale_factor to both axes. Only scale points along the bone's primary directional axis; leave cross-axis thickness at a $1.0$ scale multiplier:Arms (Primary Axis: $X$):$$\text{point}*{\text{local}}.x = (P*x - \text{anchor}\_x) \times \left(\frac{\text{upper_arm_length}}{\text{part_length}}\right)$$$$\text{point}*{\text{local}}.y = P*y - \text{anchor}\_y$$Legs / Torso (Primary Axis: $Y$):$$\text{point}*{\text{local}}.x = P*x - \text{anchor}\_x$$$$\text{point}*{\text{local}}.y = (P*y - \text{anchor}\_y) \times \left(\frac{\text{upper_leg_length}}{\text{part_length}}\right)$$Head: Keep unscaled ($1.0$ factor) on both axes:$$\text{point}*{\text{local}} = P - \text{anchor}$$(Guard against division by zero if part_length <= 0 by defaulting the scale factor to 1.0.)2. Joint-Based Anchor Alignment (Not BBox Center)Replace bounding-box midpoint anchors with joint origins so shapes rotate correctly around the bone joints:Head Anchor: Bottom-center of bounding box (x = (min_x + max_x) / 2, y = max_y).Torso & Legs Anchor: Top-center of bounding box (x = (min_x + max_x) / 2, y = min_y).Arms Anchor: Joint-end connection (x = min_x for right arms, x = max_x for left arms, y = (min_y + max_y) / 2).3. Node Transform ResetEnsure target Body/\* container nodes have their local scale reset to Vector2(1, 1) and rotation = 0 so Godot's
|
||
|
||
## Stickman editor (Phase 9 Round 4)
|
||
|
||
> FIXED — 2026 Round 4: implemented per docs/phase9_round4_bugfix_spec.md; the mounted head geometry is dropped +28 px (`HEAD_CHIN_DROP`) in rig space so the chin lands 28 px below the neck, aligned with the editor silhouette guide's head circle bottom and overlapping the torso. Verified with a 15-assertion headless smoke test.
|
||
|
||
1. **Head too high in the harness.** The head bone origin (neck) is at `y = −391.5`, and the mounted head's chin (its local origin) previously landed on that neck joint, so the head floated above the torso with no overlap. The editor's silhouette guide draws the head as a radius-100 circle centered at the Head joint `(0, −463.5)`, so the circle's bottom is `−363.5` — 28 px below the neck, overlapping the torso's top region. Fixed: the mounted head points now receive a `Vector2(0.0, 28.0)` rig-space translation after the part transform and the (θ = 0, s = 1) transform, so the chin lands at world ≈ `(0, −363.5)` — the guide circle's bottom — overlapping the torso by 28 px, matching the editor guide. The drop is a rig-space fixture position (applied after part scale/rotation), so a flipped/rotated head drops identically. node hierarchy does not multiply the geometry scaling a second time.Deliverable:Please update StkRigAdapter.gd to implement these corrected coordinate transform calculations and return the full updated GDScript.
|
||
|
||
## Stickman editor (Phase 9 Round 5)
|
||
|
||
> ADDED — 2026 Round 5: implemented per docs/phase9_round5_bugfix_spec.md; the harness now reproduces the editor's guide-relative part placement **1:1**. The editor exports per-part `guide_offset` (bbox center − guide joint, preview space; a pure master-space delta) as **write-only** metadata on save (`FILE_VERSION` bumped to `"1.5"`; the load path ignores the key). The head maps to the guide **Neck** joint — the head bone's rig attachment origin `(0, −391.5)` — **not** the circle center, which sits 72 px above the neck. `StkRigAdapter` applies the offset as a node-frame translation `t = (guide_offset + (A − C)).rotated(−c_node)` only when the key is present (A = the mount anchor incl. the 180° flip rule, C = the raw bbox center, `c_node` = the part's driver `RemoteTransform2D.global_rotation` at apply time); old files without the key keep the previous offset-0 behavior and the head falls back to the Round 4 `HEAD_CHIN_DROP`. **Note:** `.stk` files must be re-saved from the editor to populate the offsets — only files carrying `guide_offset` pick up the guide-relative placement in the harness.
|
||
|
||
## Stickman editor (Phase 9 Round 6)
|
||
|
||
> FIXED — 2026 Round 6: implemented per docs/phase9_round6_bugfix_spec.md; the mount anchor is now the transformed end nearest the part's stored guide joint when `guide_offset` is present. Verified with a 32-assertion headless smoke test.
|
||
|
||
1. **Lower-left-leg mounted 180° off its bone.** The leg's drawn knee was at the `min_x` end, but the per-side family rule anchored it at the `max_x` end, so the ankle attached at the joint and the shin hung backward. Root cause: the adapter picked the joint end with fixed per-side family rules (left limbs → `max_x`, right limbs → `min_x`) plus a 180° flip heuristic, but the user's drawn-side conventions are **inconsistent across parts** — no fixed rule can know which drawn end is the knee. Fix: when a part carries `guide_offset`, the anchor is whichever transformed end (`E(J_raw)` or `E(F_pt_raw)`) is **nearest `center − guide_offset`** (the part's guide joint) — the knee is now at the joint. Old files without the key keep the previous family rules.
|
||
2. **Lower-right-arm mounted 180° off its bone.** Same root cause mirrored on the right: the drawn elbow was at the `max_x` end, but the family rule anchored the arm at the `min_x` end, so the wrist attached at the joint and the forearm hung backward. Fix: the same nearest-end-to-guide-joint anchor selection puts the elbow at the joint. Old files without the key keep the previous rules.
|
||
|
||
## Stickman editor (Phase 9 Round 7)
|
||
|
||
> ADDED — 2026 Round 7: implemented per docs/phase9_round7_feature_spec.md; the test harness now exposes **six** draggable IK handles. `IK_HANDLE_PATHS` gains `"Head"` (`IK_Targets/Head`, the `SkeletonModification2DLookAt` aim point) and `"Torso"` (`IK_Targets/Torso`, whose child `RemoteTransform2D` moves the hip bone). Dragging the **Torso** handle moves **bones only** — the marker's `RemoteTransform2D` translates the hip bone and the whole skeleton + `Body/*` visuals follow rigidly, while the limb/head targets stay put (dragging the figure away from them stretches the limbs toward the stationary targets, per user decision). Dragging the **Head** handle drives the Head bone's LookAt rotation (clamped at the authored ~55° constraint); `Body/Head` follows. `_handle_color()` colors the head marker yellow (`HANDLE_COLOR_HEAD`) and the torso marker magenta (`HANDLE_COLOR_TORSO`); hands stay green, feet blue. The IK overlay additionally draws a null-guarded semi-transparent yellow aim line from the Head bone origin to the head marker (visual aid for the LookAt test). Verified with a 17-assertion headless test (Torso moved by (60, −40) → `Skeleton2D/Torso` and `Body/*` translate by exactly (60, −40); Head marker moved → Head bone + `Body/Head` rotate).
|
||
|
||
## Sandbox Stage — Director Tool (Phase 3a)
|
||
|
||
> FIXED — 2026-08-29: `walk_to` no longer stops after a few pixels. `_update_walking` now defers all nav reads until `NavigationServer2D.map_get_iteration_id(...) != 0` (map-sync guard), forces the path query via `get_next_path_position()` before any empty-path/finished check, and consumes the empty-path grace (`_walk_path_grace = 2`) only after map sync. Off-by-default diagnostics added: `DEBUG_WALK` + `_walk_dbg()` in `stickman_rig.gd`, `DEBUG_STAGE` + `_stage_dbg()` in `sandbox_stage.gd`. Verified with a 44-assertion headless regression suite. **Follow-up (2026-08-29):** the initial "warn + finish in place" unreachable-target policy was itself reported as "stickman stands still with a waypoint" and was replaced by **hybrid nav/direct steering** — on-mesh targets follow the nav path (`_walk_mode = "nav"`), off-mesh/unreachable targets switch to straight-line direct steering toward the clicked waypoint (`_walk_mode = "direct"`, root target = waypoint + `FOOT_OFFSET`). No `push_warning` on off-mesh (a supported case, logged only via `_walk_dbg`); `_walk_path_grace` removed (map-sync guard + forced path query replace it); the debug trace now includes `mode=nav|direct`.
|
||
|
||
1. **`walk_to` stops after a few pixels.** A stickman directed to walk stops ~5 px into the move and then either sits still or is treated as finished, instead of walking to the target.
|
||
- **Symptom:** in Play, the stickman advances one `move_toward` step then stops; the walk never reaches its target.
|
||
- **Root cause:** `_update_walking` checked `is_navigation_finished()` before the map had synchronized. An unsynced `NavigationAgent2D` (map iteration id `0`) reports an empty, already-finished path, so the walk was ended after a single step. Additionally, `get_current_navigation_path()` alone never triggers a path computation — only `get_next_path_position()` forces the agent's internal `_update_navigation()` to re-query the map for a fresh target, so the empty-path guard would misjudge an off-mesh target as "unreachable" immediately.
|
||
- **Fix:** (1) defer all nav reads until `map_get_iteration_id(...) != 0`; (2) call `get_next_path_position()` **before** the empty-path / finished checks so the path is actually computed; (3) make walking **hybrid** — an on-mesh target follows the nav path (`_walk_mode = "nav"`), while an off-mesh/unreachable target (path empty **or** `is_target_reachable()` false) switches to **direct straight-line steering** toward the clicked waypoint (`_walk_mode = "direct"`, root target = waypoint + `FOOT_OFFSET`), so the stickman always reaches the waypoint the user clicked; (4) **no** `push_warning` for an off-mesh waypoint (a supported case — logged only via `_walk_dbg`); the original "warn + finish in place" policy was itself reported as "stickman stands still with a waypoint" and was superseded by this DIRECT branch; (5) `_finish_walk(reason: String)` internal param for the debug trace. The `_walk_path_grace` counter is **removed** — the map-sync guard + forced path query replace it.
|