完善客户端功能并同步差距文档

This commit is contained in:
shenlei
2026-09-03 18:40:01 +09:00
parent f93a172296
commit 13ccb8d02d
135 changed files with 21881 additions and 1259 deletions
+144
View File
@@ -0,0 +1,144 @@
# player_move_test —— §3.1 Src/Dst 移动模型(player_controller.gdheadless 自检。
# godot --headless --path project --script player_move_test.gd
# 覆盖:NEW_Goto 设 Src/Dst/is_going、__IsMovableGroundDistance 太近不动、
# 三门(frozen/lock/moving_skill)、__ReserveClickGround 的 0.1s delay 重试。
extends SceneTree
const PlayerCtl = preload("res://player_controller.gd")
var _fail := 0
func _ck(c: bool, m: String) -> void:
if not c:
_fail += 1
printerr("FAIL: " + m)
func _init() -> void:
await _run()
if _fail == 0:
print("PASS: player_move_test (Src/Dst move model / gates / reserved ground)")
quit(0)
else:
printerr("%d check(s) failed" % _fail)
quit(1)
func _run() -> void:
# SceneTree._init() 早于 root 就绪;等一帧再建 Node3D,避免 !is_inside_tree()。
await process_frame
var pc: Node = PlayerCtl.new()
get_root().add_child(pc)
pc.player = Node3D.new()
get_root().add_child(pc.player)
pc.player.position = Vector3.ZERO
# 1) NEW_Goto:远点 -> 设 Dst + is_going + 面向
var ok: bool = pc._goto(Vector3(10, 0, 0))
_ck(ok, "_goto(far) returns true")
_ck(pc.is_going(), "_goto -> is_going()")
_ck(pc._dst_pos.is_equal_approx(Vector3(10, 0, 0)), "Dst set to click point")
_ck(pc._src_pos.is_equal_approx(Vector3.ZERO), "Src set to current pos")
_ck(is_equal_approx(pc._dst_rot, atan2(10.0, 0.0)), "_dst_rot faces the target")
# 2) __IsMovableGroundDistance:太近(< 1 m)不动
pc._is_going = false
var near_ok: bool = pc._goto(Vector3(0.5, 0, 0))
_ck(near_ok, "_goto(near) returns true (treated as done)")
_ck(not pc.is_going(), "within MOVABLE_GROUND_DISTANCE -> no movement")
# 3) 每帧推进:朝 Dst 走,抵达后 is_going 归零
pc._goto(Vector3(3, 0, 0))
for i in 240:
pc._process(0.05)
if not pc.is_going():
break
_ck(pc.player.position.distance_to(Vector3(3, 0, 0)) < 0.5, "advances to Dst, got %s" % pc.player.position)
_ck(not pc.is_going(), "arrival clears is_going")
# 4) 三门:frozen / lock -> _goto 被挡 -> 预约 + 0.1s 后重试
pc.player.position = Vector3.ZERO
pc._is_going = false
pc.frozen = true
var blocked: bool = pc._goto(Vector3(8, 0, 0))
_ck(not blocked, "_goto while frozen returns false (gated)")
_ck(not pc.is_going(), "frozen -> not going yet")
_ck(pc._reserved_ground != null, "frozen -> click ground reserved")
# 冻结中推进:不平移、不消预约
pc._process(0.05)
_ck(pc.player.position.is_equal_approx(Vector3.ZERO), "frozen -> no translation")
# 解冻:delay 递减到 0 后自动重试预约点
pc.frozen = false
for i in 10:
pc._process(0.02)
_ck(pc.is_going() or pc.player.position.x > 0.1,
"reserved ground fires after delay once unfrozen (going=%s x=%.2f)" % [pc.is_going(), pc.player.position.x])
# 5) moving_skill:只转向不平移
pc.player.position = Vector3.ZERO
pc._is_going = false
pc.moving_skill = true
pc._goto(Vector3(0, 0, 6))
_ck(not pc.is_going(), "_goto while moving_skill is gated (no translate)")
pc.moving_skill = false
# 清掉第 5 组留下的预约点,免得下面各组被它改向。
pc._reserved_ground = null
pc._reserved_delay_time = 0.0
# 6) Actor-Actor 碰撞(TestActorCollision / BlockMovement 近似)——正前方有远端 Actor
# 时走不过去,停在两 body 球相接前;is_going 归零(BlockMovement = 不滑移硬停)。
pc.player.position = Vector3.ZERO
pc.player.rotation.y = 0.0
pc._is_going = false
pc.skip_actor_collision = false
var blocker := Node3D.new()
get_root().add_child(blocker)
blocker.global_position = Vector3(2, 0, 0) # 正前方 2 m
pc.pickables.append(blocker)
pc._goto(Vector3(10, 0, 0)) # 想穿过 blocker
for i in 240:
pc._process(0.05)
if not pc.is_going():
break
_ck(pc.player.position.x > 0.3 and pc.player.position.x < 1.5,
"actor collision halts before overlap (x=%.2f)" % pc.player.position.x)
_ck(not pc.is_going(), "blocked advance clears is_going (BlockMovement)")
# 6b) 死亡的 Actor 不挡(rVictim.IsDead()
pc.player.position = Vector3.ZERO
pc._is_going = false
blocker.set_meta("dead", true)
pc._goto(Vector3(10, 0, 0))
for i in 400:
pc._process(0.05)
if not pc.is_going():
break
_ck(pc.player.position.distance_to(Vector3(10, 0, 0)) < 0.6,
"dead actor does not block (x=%.2f)" % pc.player.position.x)
blocker.set_meta("dead", false)
# 6c) CanSkipCollisionskip_actor_collision)为真 → 整段跳过,可穿过
pc.player.position = Vector3.ZERO
pc._is_going = false
pc.skip_actor_collision = true
pc._goto(Vector3(10, 0, 0))
for i in 400:
pc._process(0.05)
if not pc.is_going():
break
_ck(pc.player.position.distance_to(Vector3(10, 0, 0)) < 0.6,
"skip_actor_collision passes straight through (x=%.2f)" % pc.player.position.x)
pc.skip_actor_collision = false
pc.pickables.erase(blocker)
blocker.free()
# 7) 坐骑转向速度:MountHorse → c_fDefaultHorseRotationSpeed(300),逐帧收敛不瞬转
_ck(is_equal_approx(pc.rotation_speed_deg, pc.ROT_SPEED_DEFAULT_DEG),
"default rotation speed is c_fDefaultRotationSpeed 1200")
pc.rotation_speed_deg = pc.ROT_SPEED_HORSE_DEG
pc.player.position = Vector3.ZERO
pc.player.rotation.y = 0.0
pc._is_going = false
pc._goto(Vector3(0, 0, -6)) # 需要转到 yaw ≈ ±PI
pc._process(0.05)
_ck(absf(pc.player.rotation.y) <= deg_to_rad(pc.ROT_SPEED_HORSE_DEG) * 0.05 + 1e-3,
"mounted: turn clamped to 300 deg/s in one 50ms step (%.3f rad)" % pc.player.rotation.y)
_ck(pc.player.position.z < -0.05, "mounted: still translates toward Dst while turning")
pc.rotation_speed_deg = pc.ROT_SPEED_DEFAULT_DEG