Files
mtgodot-poc/project/test_wasd_steering_parity.gd
T
2026-09-21 16:38:58 -07:00

133 lines
5.0 KiB
GDScript

# 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 0: 40250 按键事件状态,不继承进入游戏前的系统按键
# -------------------------------------------------------------------------
print("\n--- Test 0: 进入游戏按键状态与 W/S 优先级 ---")
pc.stop()
_ck(pc._wasd() == Vector2.ZERO, "fresh/stop state cannot auto-move on game entry")
var w_down := InputEventKey.new()
w_down.keycode = KEY_W
w_down.pressed = true
pc.active = false
pc._unhandled_input(w_down)
pc.active = true
_ck(pc._wasd() == Vector2.ZERO, "W pressed during loading is not inherited by the game phase")
pc._unhandled_input(w_down)
_ck(pc._wasd() == Vector2(0, -1), "W KeyDown starts forward movement")
var s_down := InputEventKey.new()
s_down.keycode = KEY_S
s_down.pressed = true
pc._unhandled_input(s_down)
_ck(pc._wasd() == Vector2(0, -1), "W keeps priority while W and S overlap")
var w_up := InputEventKey.new()
w_up.keycode = KEY_W
w_up.pressed = false
pc._input(w_up)
_ck(pc._wasd() == Vector2(0, 1), "releasing W switches cleanly to held S")
pc.stop()
_ck(pc._wasd() == Vector2.ZERO, "stop clears all latched direction keys")
# -------------------------------------------------------------------------
# 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")