Refactor stickman rig and ragdoll mechanics; enhance pose restoration and stability
- Disabled constraints for the stickman rig's head bone to allow for smoother animations. - Improved mode transitions in the sandbox stage to preserve stickman poses between EDIT and DIRECT modes. - Added side profile stabilization for knee bends in the ragdoll component to prevent unnatural poses during physics interactions. - Enhanced the snap-to-standing functionality to restore the stickman's original pose accurately. - Introduced new tests for validating pose preservation during mode transitions and ragdoll behavior during walking animations. - Created a new scene for testing stickman bending and ragdoll transitions.
This commit is contained in:
@@ -77,3 +77,122 @@ Update path references across project markdown files:
|
||||
3. **Test Harness Verification**: Run `res://scenes/test_harness.tscn` via **F6** to ensure walk, speak, wait, and ragdoll actions execute cleanly.
|
||||
|
||||
4. **Automated Suite**: Run the full project unit test suite in `tests/` to confirm all assertions pass.
|
||||
|
||||
---
|
||||
|
||||
### **Completion Record (verified implementation)**
|
||||
|
||||
Phase 8 is implemented. The relocated scene is `res://scenes/stickman_rig.tscn`; its root `Master` node is a `StickmanRig` facade over three authored component children. The facade remains the compatibility boundary for callers while each component owns one subsystem:
|
||||
|
||||
```text
|
||||
Master (StickmanRig)
|
||||
├── RigComponents
|
||||
│ ├── Locomotion (StickmanLocomotion)
|
||||
│ │ ├── terrain probing / ground following
|
||||
│ │ ├── walking and airborne motion
|
||||
│ │ └── collision proxies and landing reactions
|
||||
│ ├── Ragdoll (StickmanRagdoll)
|
||||
│ │ ├── procedural RigidBody2D + PinJoint2D graph
|
||||
│ │ ├── rest detection and momentum handoff
|
||||
│ │ └── capture, re-anchor, and stand-up recovery
|
||||
│ └── ActionRunner (StickmanActionRunner)
|
||||
│ ├── queued walk / speak / wait actions
|
||||
│ └── ragdoll and recovery requests
|
||||
├── Body/* mounted visuals, driven by RemoteTransform2D nodes
|
||||
├── Skeleton2D/Torso/* Bone2D chain and IK modifications
|
||||
├── IK_Targets/* six animation/recovery markers
|
||||
└── AnimationPlayer canonical walk clips and authored animation data
|
||||
```
|
||||
|
||||
`StickmanRig` owns cross-cutting state (`ANIMATED`, `RAGDOLL`, `RECOVERING`), facing profiles, bend flags, body z-order, and signal/API forwarding. `StickmanRagdoll` owns physics construction and recovery; `StickmanLocomotion` owns terrain/motion; `StickmanActionRunner` owns director queue execution. Component references are resolved and configured in `StickmanRig._ready()` and are null-guarded with warnings.
|
||||
|
||||
#### Ragdoll handoff and recovery
|
||||
|
||||
The runtime handoff preserves the current solved IK pose before physics takes ownership:
|
||||
|
||||
```text
|
||||
ANIMATED
|
||||
│ set_ragdoll(true)
|
||||
▼
|
||||
capture pre-ragdoll markers, bone rotations, facing, bend flags, knee sides
|
||||
│ build bodies from current bone endpoints; copy mounted Body/* visuals
|
||||
▼
|
||||
RAGDOLL ── rest + timeout ──▶ capture body pose + probe terrain
|
||||
│ destroy physics graph
|
||||
│ re-anchor root at captured landing feet
|
||||
▼
|
||||
RECOVERING ── tween markers/bones toward captured entry pose ──▶ ANIMATED
|
||||
```
|
||||
|
||||
The important dictionaries have these shapes (vector values are runtime `Vector2`s):
|
||||
|
||||
```gdscript
|
||||
_spawn_pose = {
|
||||
"Torso": {"pos": captured_torso_pos, "rot": captured_torso_rotation},
|
||||
"Head": {"pos": captured_head_tip, "rot": 0.0},
|
||||
"Left_Hand": {"pos": captured_left_wrist, "rot": 0.0},
|
||||
"Right_Hand": {"pos": captured_right_wrist, "rot": 0.0},
|
||||
"Left_Leg": {"pos": captured_left_ankle, "rot": 0.0},
|
||||
"Right_Leg": {"pos": captured_right_ankle, "rot": 0.0},
|
||||
}
|
||||
|
||||
_captured_pose["left_lower_leg"] = {
|
||||
"pos": rig_local_body_position,
|
||||
"rot": body_global_rotation - rig_global_rotation,
|
||||
"half": capsule_half_length,
|
||||
}
|
||||
```
|
||||
|
||||
`_spawn_pose` is initialized from the adapted skeleton by `StickmanFactory.spawn_from_data()` and is the placement ghost's authored pose; the values therefore depend on the loaded `.stk`, rather than on `STAND_POSE`. `_pre_ragdoll_state` is the recovery endpoint: it stores the six markers, limb/head rotations, facing profile, bend flags, head LookAt state, knee-side information, and chain-space offsets. `_captured_pose` is the fallen physics pose used to reconstruct the skeleton and landing position.
|
||||
|
||||
#### Stage mode flow and pose preservation
|
||||
|
||||
EDIT and DIRECT are two views of the same frozen, non-simulating world. The mode transition receives `previous_mode` so it can avoid an unnecessary reset:
|
||||
|
||||
```text
|
||||
EDIT ◄──────────────► DIRECT
|
||||
│ │
|
||||
└──────────┬───────────┘
|
||||
▼
|
||||
PLAY
|
||||
│
|
||||
set_mode(EDIT or DIRECT)
|
||||
│
|
||||
▼
|
||||
cleanup + restore spawn pose
|
||||
```
|
||||
|
||||
| Transition | Stage behavior | Rig pose behavior |
|
||||
|---|---|---|
|
||||
| EDIT → DIRECT | Hide edit controls; keep world frozen | Preserve exact live pose and IK markers |
|
||||
| DIRECT → EDIT | Restore edit controls; keep world frozen | Preserve exact live pose and IK markers |
|
||||
| EDIT/DIRECT → PLAY | Enable simulation, terrain following, and action queues | Begin from the current authored/editor pose |
|
||||
| PLAY → EDIT/DIRECT | Stop queues, disable terrain following, freeze physics, restore object transforms | `snap_to_standing()` restores the factory-captured `_spawn_pose` |
|
||||
|
||||
The root cause of the verified regression was that `snap_to_standing()` restored the legacy hardcoded `STAND_POSE`. That generic pose lowered the arms and rotated the head whenever PLAY cleanup ran. The central fix is to restore `_spawn_pose` when it exists, with `STAND_POSE` retained only as a compatibility fallback for rigs that have no captured spawn pose. Ragdoll recovery is separate: its stand-up tween still targets `_pre_ragdoll_state`, so recovery returns to the pose captured at ragdoll entry rather than the factory pose.
|
||||
|
||||
#### Facing, walk-phase bend stabilization, and head tracking
|
||||
|
||||
`FacingProfile.LEFT` is a whole-rig X mirror (`Master.scale.x = -1`); RIGHT and FORWARD use identity scale. Runtime walking always plays the canonical `walk_right` clip and mirrors it for LEFT. The profile still applies provisional per-joint bend flags and z-order tables. For side profiles, a walk keyframe can place a leg exactly straight, leaving PinJoint2D with no geometric knee side. The ragdoll component therefore:
|
||||
|
||||
- permits the straight hinge boundary (`0°`) and the intended fold range (`±150°`) for LEFT/RIGHT profiles;
|
||||
- adds only a small 3° profile-relative bias at the near-straight ragdoll handoff; and
|
||||
- checks each rendered RAGDOLL frame, correcting only an actual wrong-side knee crossing while preserving normal physical motion.
|
||||
|
||||
Head tracking is owned by the `SkeletonModification2DLookAt` targeting `IK_Targets/Head`. RIGHT/FORWARD enable it. LEFT disables LookAt and pins the canonical head rotation because the whole-rig mirror must remain mirror-invariant; this is intentional rather than a missing target update. The scene's authored head constraint is disabled so the active LookAt can follow moved markers without the old constraint interpretation pinning it.
|
||||
|
||||
#### Regression coverage
|
||||
|
||||
The completed implementation was checked with focused headless tests and the existing suites:
|
||||
|
||||
| Test | Result | Coverage |
|
||||
|---|---:|---|
|
||||
| `tests/test_phase8_rig_modularization.gd` | 23/23 | relocated scene, component ownership, head tracking, facing flags, ragdoll facade |
|
||||
| `tests/test_phase8_edit_direct_pose.gd` | 45/45 | EDIT↔DIRECT round trip and EDIT→PLAY→DIRECT pose/marker restoration |
|
||||
| `tests/test_phase8_ragdoll_walk_handoff.gd` | 80/80 | LEFT/RIGHT profiles across multiple walk animation phases and physics frames |
|
||||
| `tests/test_phase45_collision_bodies.gd` | 223/223 | collision proxies, reactions, and PLAY cleanup to authored pose |
|
||||
| `tests/test_phase3c_walk_recovery.gd` | 185/185 | walk, ragdoll, recovery, and action integration |
|
||||
| `tests/test_phase6a_airborne_landing.gd` | 24/24 | airborne pose, landing, and ragdoll cleanup |
|
||||
| `tests/test_phase4b_stage.gd` | 53/53 | stage mode UI/state transitions and pending-action cleanup |
|
||||
|
||||
The plan's original acceptance criteria remain the historical intent; this record captures the as-built ownership and the follow-up fixes verified after user testing.
|
||||
|
||||
Reference in New Issue
Block a user