Files
stickman/tests/test_phase8_ragdoll_walk_handoff.gd
T

91 lines
3.4 KiB
GDScript

extends SceneTree
const STAGE_SCENE := preload("res://scenes/stage/sandbox_stage.tscn")
const WALK_PHASES: Array[float] = [0.0, 0.08, 0.16, 0.24, 0.32, 0.40, 0.48, 0.56, 0.64, 0.72]
const SETTLE_FRAMES := 30
var _checks := 0
var _failures := 0
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
for profile: int in [StickmanRig.FacingProfile.RIGHT, StickmanRig.FacingProfile.LEFT]:
for walk_phase: float in WALK_PHASES:
await _check_handoff(profile, walk_phase)
if _failures == 0:
print("RESULT: ALL PASSED (%d assertions)" % _checks)
quit(0)
else:
print("RESULT: %d FAILURE(S) out of %d assertions" % [_failures, _checks])
quit(1)
func _check_handoff(profile: int, walk_phase: float) -> void:
var stage := STAGE_SCENE.instantiate() as Node2D
root.add_child(stage)
stage._snap_enabled = false
var rig: StickmanRig = stage._spawner.spawn("stickman", Vector2.ZERO)
rig.auto_recover = false
var destination_x := 500.0 if profile == StickmanRig.FacingProfile.RIGHT else -500.0
rig.walk_to(Vector2(destination_x, 0.0))
var animation := rig.get_node("AnimationPlayer") as AnimationPlayer
animation.seek(walk_phase, true)
await process_frame
await physics_frame
var label := "%s @ %.2fs" % [StickmanRig.FacingProfile.keys()[profile], walk_phase]
_check(rig.get_facing_profile() == profile, "%s keeps its facing profile before ragdoll" % label)
rig.set_ragdoll(true)
var ragdoll := rig.get_node("RigComponents/Ragdoll") as StickmanRagdoll
var expected_side := -1.0 if profile == StickmanRig.FacingProfile.RIGHT else 1.0
_check(_both_legs_match(_ragdoll_leg_sides(ragdoll), expected_side),
"%s enters ragdoll with both knees on the profile side" % label)
for frame in SETTLE_FRAMES:
await physics_frame
await process_frame
# SceneTree.process_frame is emitted before Node._process callbacks. Wait one
# more idle frame so the rendered pose includes the post-physics knee guard.
await process_frame
var settled_sides := _ragdoll_leg_sides(ragdoll)
_check(_both_legs_match(settled_sides, expected_side),
"%s keeps both knees on the profile side for %d frames (sides %s)"
% [label, SETTLE_FRAMES, str(settled_sides)])
_check(rig.get_facing_profile() == profile, "%s never changes facing profile" % label)
stage.queue_free()
await process_frame
func _ragdoll_leg_sides(ragdoll: StickmanRagdoll) -> Dictionary:
var bodies := ragdoll.get_bodies()
var result := {}
for prefix: String in ["left", "right"]:
var upper := bodies.get(prefix + "_upper_leg") as RigidBody2D
var lower := bodies.get(prefix + "_lower_leg") as RigidBody2D
var upper_half := float(upper.get_meta("half_height", 0.0))
var lower_half := float(lower.get_meta("half_height", 0.0))
var upper_dir := Vector2.from_angle(upper.global_rotation)
var lower_dir := Vector2.from_angle(lower.global_rotation)
var hip := upper.global_position - upper_dir * upper_half
var knee := upper.global_position + upper_dir * upper_half
var ankle := lower.global_position + lower_dir * lower_half
result["LeftLeg" if prefix == "left" else "RightLeg"] = \
signf((ankle - hip).cross(knee - hip))
return result
func _both_legs_match(sides: Dictionary, expected_side: float) -> bool:
return float(sides.get("LeftLeg", 0.0)) == expected_side \
and float(sides.get("RightLeg", 0.0)) == expected_side
func _check(condition: bool, label: String) -> void:
_checks += 1
if condition:
print("PASS: %s" % label)
else:
_failures += 1
push_error("FAIL: %s" % label)