Files
Papay-Shooter/debug/transition_check.gd
T
2026-08-02 02:20:02 -04:00

116 lines
3.8 KiB
GDScript

extends SceneTree
## Regression for the original "slide, then walk, then run" locomotion bug.
##
## godot --headless --path . -s res://debug/transition_check.gd -- [skin_glb]
##
## Ground acceleration is the same exponential response as state_ground.gd.
## The test proves that the authored BlendSpace receives that physical speed on
## the same frame, including the first high-acceleration frame and the stop
## frame. It also proves that the removed procedural lean/brake layer stays off.
const RESPONSE := 60.0
const TOP := 11.0
const DT := 1.0 / 60.0
const RUN_FRAMES := 24
var _frames := 0
var _model: SkinnedPlayerModel = null
var _speed := 0.0
var _first_speed := -1.0
var _first_stride := -1.0
var _worst_error := 0.0
var _stop_stride := -1.0
var _manual_tick := false
var _foot_bone := -1
var _foot_rotation_start := Quaternion.IDENTITY
var _foot_rotation_delta := 0.0
func _initialize() -> void:
var args := OS.get_cmdline_user_args()
var path: String = args[0] if args.size() > 0 \
else "res://assets/characters/skins/miku.glb"
var scene := Node3D.new()
root.add_child(scene)
current_scene = scene
_model = SkinnedPlayerModel.new()
_model.model_path = path
scene.add_child(_model)
func _process(_delta: float) -> bool:
_frames += 1
if _frames < 8 or not _model.loaded:
return false
if not _manual_tick:
_model.set_process(false)
_manual_tick = true
_foot_bone = _model._role_bone("foot.L",
["foot.L", "LeftFoot", "Left ankle"])
var sample := _frames - 8
if sample < RUN_FRAMES:
var response := 1.0 - exp(-RESPONSE * DT)
_speed = lerpf(_speed, TOP, response)
else:
_speed = 0.0
# Same order as the live controller: direction first, then physical speed.
_model.set_locomotion(0.0, 1.0 if _speed > 0.01 else 0.0, 0.0)
_model.update_state("ground", _speed, false)
_model._process(DT)
var stride := _model.locomotion_effective_speed_debug()
_worst_error = maxf(_worst_error, absf(stride - _speed))
if _foot_bone >= 0:
var foot_rotation := _model.skeleton.get_bone_pose_rotation(_foot_bone)
if sample == 1:
_foot_rotation_start = foot_rotation
elif sample > 1 and sample < RUN_FRAMES:
_foot_rotation_delta = maxf(_foot_rotation_delta,
_foot_rotation_start.angle_to(foot_rotation))
if sample == 0:
_first_speed = _speed
_first_stride = stride
if sample == RUN_FRAMES:
_stop_stride = stride
if sample > RUN_FRAMES + 2:
print("\n=== velocity-driven locomotion ===")
print(" first frame: body %.3f m/s, authored stride %.3f m/s" \
% [_first_speed, _first_stride])
print(" worst stride/speed error: %.5f m/s" % _worst_error)
print(" stop-frame stride: %.5f m/s" % _stop_stride)
print(" animated foot rotation delta: %.5f rad" % _foot_rotation_delta)
print(" procedural lean %.5f, brake %.5f" \
% [_model.get_lean_debug(), _model.get_brake_debug()])
var failures: Array[String] = []
if _first_speed < 6.0:
failures.append("test did not reproduce high first-frame acceleration")
if absf(_first_stride - _first_speed) > 0.02:
failures.append("legs did not receive first-frame physical speed")
if _worst_error > 0.02:
failures.append("authored stride diverged from physical speed")
if _stop_stride > 0.02:
failures.append("legs did not reach Idle on the stop frame")
if _foot_bone < 0:
failures.append("could not resolve a foot bone")
elif _foot_rotation_delta < 0.03:
failures.append("runtime foot pose stayed static")
if absf(_model.get_lean_debug()) > 0.02:
failures.append("procedural locomotion lean is still active")
if absf(_model.get_brake_debug()) > 0.02:
failures.append("procedural stop pose is still active")
if failures.is_empty():
print("TRANSITION_CHECK PASS: same-frame, authored locomotion\n")
quit(0)
else:
for failure in failures:
push_error("TRANSITION_CHECK FAIL: " + failure)
quit(1)
return true
return false