# test_wasd_steering_parity.gd —— 验证 WASD 转向与连续方向切换对齐 40250: # 1. 连续快速切换方向(W -> S, A -> D, 对角线切换等),俯仰角(X)与翻滚角(Z)始终保持 0,无翻跟头/倾倒。 # 2. 偏航角(Y)平滑过渡且严格归一化在 [-PI, PI],越过 ±PI 边界时不发生每帧方向往复震荡。 # 3. 40250 按键优先级(Up 优先于 Down,Left 优先于 Right):切换瞬间两键同时按住时不归零卡顿。 # # godot --headless --path project --script test_wasd_steering_parity.gd extends SceneTree const PlayerController = preload("res://player_controller.gd") var _fail := 0 func _ck(c: bool, m: String) -> void: if not c: _fail += 1 printerr("FAIL: " + m) else: print(" [PASS] " + m) func _init() -> void: await process_frame _run() if _fail == 0: print("=== ALL WASD STEERING PARITY TESTS PASSED ===") quit(0) else: printerr("=== FAILED: %d check(s) failed ===" % _fail) quit(1) func _run() -> void: var pc := PlayerController.new() var player := Node3D.new() get_root().add_child(player) get_root().add_child(pc) pc.player = player pc.rotation_speed_deg = 1200.0 # ------------------------------------------------------------------------- # Test 1: 边界跨越平滑度与无翻跟头(X/Z 严格为 0) # ------------------------------------------------------------------------- print("\n--- Test 1: 边界跨越(±PI)平滑转向与 X/Z 稳定性 ---") player.rotation = Vector3.ZERO # 模拟从北(+PI)到西(-PI/2)再到南(0)再到东(PI/2)再到北(-PI) var targets: Array[float] = [ PI - 0.05, -PI + 0.05, 0.0, PI / 2.0, -PI / 2.0, PI ] var flipped := false var xz_tilted := false for t in targets: for step in range(30): pc._turn_toward(t, 0.016) if absf(player.rotation.x) > 1e-4 or absf(player.rotation.z) > 1e-4: xz_tilted = true if absf(player.rotation.y) > PI + 1e-3: flipped = true _ck(not xz_tilted, "Player rotation.x and rotation.z strictly remain 0.0 (no pitch/roll somersault)") _ck(not flipped, "Player rotation.y strictly remains in [-PI, PI] without unbounded growth") # ------------------------------------------------------------------------- # Test 2: 180° 急转弯不发生往复震荡(单向收敛) # ------------------------------------------------------------------------- print("\n--- Test 2: 180° 急转弯单向平滑收敛 ---") player.rotation.y = 3.0 var target_yaw := -2.8 var prev_diff := 1e9 var monotonic := true for step in range(20): var cur_yaw: float = wrapf(player.rotation.y, -PI, PI) var diff: float = absf(wrapf(target_yaw - cur_yaw, -PI, PI)) pc._turn_toward(target_yaw, 0.016) var new_diff: float = absf(wrapf(target_yaw - player.rotation.y, -PI, PI)) if new_diff > diff + 1e-4: monotonic = false if is_equal_approx(player.rotation.y, target_yaw): break _ck(monotonic, "Turn toward target is monotonic across the PI boundary (no back-and-forth oscillation)") _ck(is_equal_approx(player.rotation.y, target_yaw), "Turn converges exactly to target_yaw") # ------------------------------------------------------------------------- # Test 3: 连续 WASD 快速方向切换无卡死或翻滚 # ------------------------------------------------------------------------- print("\n--- Test 3: 连续高频 WASD 切换稳定性 ---") var dirs := [ Vector3(0, 0, -1), # W Vector3(0, 0, 1), # S Vector3(-1, 0, 0), # A Vector3(1, 0, 0), # D Vector3(-0.707, 0, -0.707), # WA Vector3(0.707, 0, 0.707), # SD Vector3(0.707, 0, -0.707), # WD Vector3(-0.707, 0, 0.707), # SA ] var ok := true for d in dirs: var yaw := atan2(d.x, d.z) for _step in range(15): pc._turn_toward(yaw, 0.016) if absf(player.rotation.x) > 1e-4 or absf(player.rotation.z) > 1e-4: ok = false _ck(ok, "Continuous 8-way rapid directional changes maintain perfect upright orientation")