Implement Phase 8: Rig Relocation and Component Modularization
- Moved the rig scene from `res://master_rig.tscn` to `res://scenes/stickman_rig.tscn`, preserving its UID. - Split the `StickmanRig` controller into three components: `StickmanLocomotion`, `StickmanRagdoll`, and `StickmanActionRunner`. - Updated the architecture to ensure `StickmanRig` acts as the public facade for the new components. - Created a new test suite `test_phase8_rig_modularization.gd` to verify the integrity of the relocated rig and its components. - Added checks for scene loading, component resolution, action queue delegation, and state authority.
This commit is contained in:
+22
-13
@@ -15,6 +15,7 @@ extends SceneTree
|
||||
|
||||
const STAGE_SCENE := preload("res://scenes/sandbox_stage.tscn")
|
||||
const RIG := preload("res://scripts/stickman_rig.gd")
|
||||
const LOCOMOTION := preload("res://scripts/stickman_locomotion.gd")
|
||||
|
||||
const SETTLE_FRAMES := 60
|
||||
const MAX_FRAMES := 300
|
||||
@@ -68,27 +69,33 @@ func _walk_case(label: String, target_feet: Vector2, expected_mode: String) -> v
|
||||
_arrived_count = 0
|
||||
rig.arrived.connect(_on_arrived)
|
||||
rig.walk_to(target_feet, 200.0)
|
||||
_check(not rig._walk_mode_latched, "%s: mode not latched before map sync" % label)
|
||||
var locomotion: StickmanLocomotion = rig.get_locomotion()
|
||||
_check(locomotion != null, "%s: locomotion component is available" % label)
|
||||
if locomotion == null:
|
||||
stage.queue_free()
|
||||
await process_frame
|
||||
return
|
||||
_check(not locomotion._walk_mode_latched, "%s: mode not latched before map sync" % label)
|
||||
var frames := 0
|
||||
while not rig._walk_done and frames < MAX_FRAMES:
|
||||
while not locomotion._walk_done and frames < MAX_FRAMES:
|
||||
await physics_frame
|
||||
frames += 1
|
||||
_check(rig._walk_done, "%s: walk finished within %d frames (used %d)" % [label, MAX_FRAMES, frames])
|
||||
_check(locomotion._walk_done, "%s: walk finished within %d frames (used %d)" % [label, MAX_FRAMES, frames])
|
||||
_check(_arrived_count == 1, "%s: exactly one arrived signal (got %d)" % [label, _arrived_count])
|
||||
_check(rig._walk_mode_latched, "%s: mode is latched" % label)
|
||||
_check(rig._walk_mode == expected_mode,
|
||||
"%s: mode latched to '%s' (got '%s')" % [label, expected_mode, rig._walk_mode])
|
||||
_check(locomotion._walk_mode_latched, "%s: mode is latched" % label)
|
||||
_check(locomotion._walk_mode == expected_mode,
|
||||
"%s: mode latched to '%s' (got '%s')" % [label, expected_mode, locomotion._walk_mode])
|
||||
# Final position equals the snapped root target (feet target + FOOT_OFFSET).
|
||||
var final_root: Vector2 = target_feet + RIG.FOOT_OFFSET
|
||||
var final_root: Vector2 = target_feet + LOCOMOTION.FOOT_OFFSET
|
||||
_check(rig.global_position.distance_to(final_root) <= 0.01,
|
||||
"%s: position snapped to final root (dist=%.3f)" % [label, rig.global_position.distance_to(final_root)])
|
||||
# Body-bob settle: Torso marker restored to STAND_POSE (0,10).
|
||||
# Completion freezes the solved final animation frame; it must not be reset
|
||||
# or drift while the root is at rest.
|
||||
var torso = rig.get_node_or_null(NodePath("IK_Targets/Torso"))
|
||||
var frozen_torso_position: Vector2 = Vector2.ZERO
|
||||
if torso != null:
|
||||
var stand_torso: Dictionary = RIG.STAND_POSE["Torso"]
|
||||
var expected_pos: Vector2 = stand_torso.get("pos", Vector2(0, 10))
|
||||
_check((torso as Node2D).position.distance_to(expected_pos) <= 1.0,
|
||||
"%s: Torso marker restored to standing pose (dist=%.3f)" % [label, (torso as Node2D).position.distance_to(expected_pos)])
|
||||
frozen_torso_position = (torso as Node2D).position
|
||||
_check(frozen_torso_position.is_finite(), "%s: Torso final animation marker is finite" % label)
|
||||
else:
|
||||
_check(false, "%s: IK_Targets/Torso missing" % label)
|
||||
# Post-arrival stability for 60 physics frames.
|
||||
@@ -101,6 +108,9 @@ func _walk_case(label: String, target_feet: Vector2, expected_mode: String) -> v
|
||||
break
|
||||
_check(settled and rig.global_position == p0,
|
||||
"%s: position unchanged for %d frames after arrival" % [label, SETTLE_FRAMES])
|
||||
if torso != null:
|
||||
_check((torso as Node2D).position.distance_to(frozen_torso_position) <= 0.01,
|
||||
"%s: Torso final animation marker remains frozen" % label)
|
||||
_check(not rig.is_walking(), "%s: is_walking false after arrival" % label)
|
||||
|
||||
stage.queue_free()
|
||||
@@ -114,4 +124,3 @@ func _check(condition: bool, message: String) -> void:
|
||||
else:
|
||||
_failures += 1
|
||||
print("FAIL: " + message)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user