From c0c066da0c30edb421905f2976a136bb76781992 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Thu, 8 Oct 2026 17:42:54 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E5=8D=A1=E2=91=A0T3?= =?UTF-8?q?=E2=80=94=E2=80=94probe=5Fgate=20--full=20=E6=AE=B5=E8=90=BD?= =?UTF-8?q?=E7=A0=81=E5=9B=9B=E6=8E=A2=E9=92=88=E6=94=B6=E7=BC=96=EF=BC=88?= =?UTF-8?q?ui=5Fprobe/ui=5Ftune=5Fprobe/t17=5Fdefer=5Fprobe/fx=5Fprobe?= =?UTF-8?q?=E2=80=94=E2=80=94=E6=B8=B8=E7=A6=BB=E9=97=B8=E5=A4=96=E5=8F=AA?= =?UTF-8?q?=E8=83=BD=E6=89=8B=E6=95=B2=E2=86=92=E5=85=A5=E9=87=8D=E9=97=B8?= =?UTF-8?q?=EF=BC=9Btimeout=20120=3D=E5=AE=9E=E6=B5=8B=2017.4/49.6/63.0/21?= =?UTF-8?q?.9s=20=E7=9A=84=202-7=C3=97=E4=BD=99=E9=87=8F=EF=BC=9BPASS=20?= =?UTF-8?q?=E8=AF=AD=E4=B9=89+=E9=80=80=E5=87=BA=E7=A0=81=E9=97=B8+?= =?UTF-8?q?=E6=AE=8B=E7=95=99=E6=89=B9=E6=9C=AB=E9=97=B8=E9=9A=8F=E9=AA=A8?= =?UTF-8?q?=E6=9E=B6=EF=BC=89+=E5=A4=B4=E6=B3=A8=E9=87=8A=E3=80=8Cshot=5Fp?= =?UTF-8?q?robe=20=E7=A6=81=E5=85=A5=E6=9C=AC=E9=97=B8=E2=80=A6=E2=80=A6?= =?UTF-8?q?=E5=BD=92=E7=9C=9F=E4=BA=BA=E7=9B=AE=E6=A3=80=E3=80=8D=E7=BA=AA?= =?UTF-8?q?=E5=BE=8B=E5=8F=A5=E5=88=A0=E9=99=A4=E6=94=B9=E5=86=99=EF=BC=88?= =?UTF-8?q?2026-10-07=20=E7=94=A8=E6=88=B7=E6=8E=88=E6=9D=83=3D=E5=8E=86?= =?UTF-8?q?=E5=8F=B2=E5=81=B7=E6=87=92=E6=A0=87=E8=AE=B0=EF=BC=9B=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E9=9D=A2=E5=BD=92=20render=5Fgate=20=E6=AD=A3?= =?UTF-8?q?=E4=B8=BB=EF=BC=89+=E8=BD=BB=E9=97=B8=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E6=AE=B5=E9=9B=B6=E5=88=A0=E5=87=8F=E9=9B=B6=E6=94=B9=E5=8A=A8?= =?UTF-8?q?=EF=BC=88--full=20=E5=8F=AA=E5=A2=9E=E4=B8=8D=E6=94=B9=EF=BC=8C?= =?UTF-8?q?=E5=A5=91=E7=BA=A6=E7=BA=A2=E7=BA=BF=E2=91=A1=EF=BC=89=E3=80=82?= =?UTF-8?q?ui=5Flayout=5Fprobe=20=E9=9B=B6=E6=96=AD=E8=A8=80=E8=AF=8A?= =?UTF-8?q?=E6=96=AD=E4=BB=B6=E8=A1=A5=E6=9C=80=E5=B0=8F=E5=B8=83=E5=B1=80?= =?UTF-8?q?=E6=96=AD=E8=A8=80=E8=BD=AC=E6=96=AD=E8=A8=80=E4=BB=B6=EF=BC=88?= =?UTF-8?q?1280/1920=20=E4=B8=A4=E6=A1=A3=EF=BC=9A=E5=9F=BA=E5=BA=A7=20128?= =?UTF-8?q?0x800/=E9=A1=B6=E5=B1=82=E4=B8=89=E5=8C=BA=E4=BA=92=E6=96=A5?= =?UTF-8?q?=E4=B8=8D=E8=B6=8A=E7=95=8C/board=5Fview=20=E5=90=AB=E4=BA=8E?= =?UTF-8?q?=20D=20=E5=8C=BA/1920=20=E6=A1=A3=E9=92=89=E8=BF=AD=E4=BB=A3=20?= =?UTF-8?q?9=20=E5=88=86=E8=BE=A8=E7=8E=87=E6=A1=88=E7=94=B2=E5=A5=91?= =?UTF-8?q?=E7=BA=A6=E2=80=94=E2=80=94stretch=3Dcanvas=5Fitems=20=E4=B8=8B?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E8=A7=86=E5=8F=A3=E4=B8=8E=E5=9B=9B=E5=8C=BA?= =?UTF-8?q?=E7=9F=A9=E5=BD=A2=E6=81=92=E5=AE=9A=EF=BC=8C=E5=88=86=E8=BE=A8?= =?UTF-8?q?=E7=8E=87=E7=BB=B4=E5=BA=A6=E4=B8=8D=E9=87=8D=E5=A4=8D=E5=BB=BA?= =?UTF-8?q?=E9=97=B8=EF=BC=9Bheadless=20resize=20=E5=AE=9E=E6=B5=8B?= =?UTF-8?q?=E7=94=9F=E6=95=88=EF=BC=9B=E9=A6=96=E8=B7=91=20PASS=20?= =?UTF-8?q?=E9=80=80=E5=87=BA=E7=A0=81=200=EF=BC=9B=E9=97=B8=E5=BD=92?= =?UTF-8?q?=E5=B1=9E=E5=BD=92=E5=8D=A1=E2=91=A2=E8=A3=81=E7=82=B9=E2=91=A1?= =?UTF-8?q?=E5=80=99=E8=A3=81=E6=9C=AC=E6=89=B9=E4=B8=8D=E5=85=A5=E9=97=B8?= =?UTF-8?q?=EF=BC=89=E3=80=82probe=5Fgate=20=E4=B8=A4=E5=BD=A2=E6=80=81?= =?UTF-8?q?=E5=AE=9E=E6=B5=8B=EF=BC=9A=E9=BB=98=E8=AE=A4=E6=AE=B5=E5=85=A8?= =?UTF-8?q?=E7=BB=BF+--click=20--full=20=E5=85=A8=E7=BB=BF=E5=9B=9B?= =?UTF-8?q?=E6=8E=A2=E9=92=88=E5=85=A8=20PASS=20=E6=AE=8B=E7=95=99=200?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/smoke/ui_layout_probe.gd | 61 ++++++++++++++++++++++++++++++++++- tools/probe_gate.sh | 56 +++++++++++++++++++++++++++++--- 2 files changed, 112 insertions(+), 5 deletions(-) diff --git a/game/smoke/ui_layout_probe.gd b/game/smoke/ui_layout_probe.gd index 582f4d6d..459a729b 100644 --- a/game/smoke/ui_layout_probe.gd +++ b/game/smoke/ui_layout_probe.gd @@ -1,6 +1,11 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # Date: 2026-09-13(四区布局诊断探针——"内容未满幅/棋盘底裁切"回归定位:打印窗口/视口/四区实际矩形 # 与最小尺寸、展开旗标,对照 battle.md §Layout 1280×800 基座规格) +# 迭代 19 卡① T5(2026-10-08):零断言诊断件补最小布局断言转断言件——1280/1920 两档: +# ①基座 1280×800(battle.md §Layout)②顶层三区(B=stage/C=info_panel/D=board_zone)互不重叠不越视口 +# ③board_view(=board_zone 子节点别名)不越视口且含于 D 区 ④1920 档钉分辨率案甲契约(迭代 9 案甲—— +# stretch=canvas_items/aspect=keep 下逻辑视口与四区矩形恒定=构图与基准档一致);退出码/PASS-FAIL 语义 +# 具备闸可接形态(闸归属归卡③裁点②候裁,本批不入闸)。原诊断打印面全保留。 # 运行:Godot_console.exe --headless --path . res://smoke/ui_layout_probe.tscn extends Node @@ -43,7 +48,61 @@ func _ready() -> void: root_box.anchor_left, root_box.anchor_top, root_box.anchor_right, root_box.anchor_bottom, root_box.offset_left, root_box.offset_top, root_box.offset_right, root_box.offset_bottom, root_box.size]) print("board_view cell_size=%s board=%s" % [main.board_view.get("cell_size"), main.board_view.board()]) - get_tree().quit(0) + + # ── 最小布局断言(迭代 19 卡① T5——1280/1920 两档;语义见头注) ── + var problems: PackedStringArray = [] + var vr: Rect2 = get_viewport().get_visible_rect() + if vr.size != Vector2(1280, 800): + problems.append("基座视口 %s ≠ 1280x800(battle.md §Layout)" % vr.size) + var azones := {"stage": main.stage, "info_panel": main.info_panel, "board_zone": main.board_zone} + var arects := {} + for k in azones: + var actl: Control = azones[k] + if actl == null: + problems.append("%s=null" % k) + continue + var ar := Rect2(actl.global_position, actl.size) + arects[k] = ar + if ar.size.x <= 0.0 or ar.size.y <= 0.0: + problems.append("%s 零尺寸 %s" % [k, ar.size]) + elif not vr.encloses(ar): + problems.append("%s 越视口 %s ⊄ %s" % [k, ar, vr]) + var bvc: Control = main.board_view + if bvc == null: + problems.append("board_view=null") + else: + var br := Rect2(bvc.global_position, bvc.size) + if not vr.encloses(br): + problems.append("board_view 越视口 %s ⊄ %s" % [br, vr]) + if arects.has("board_zone") and not arects["board_zone"].encloses(br): + problems.append("board_view 越出 board_zone(%s ⊄ %s)" % [br, arects["board_zone"]]) + var akeys := arects.keys() + for i in range(akeys.size()): + for j in range(i + 1, akeys.size()): + if arects[akeys[i]].intersects(arects[akeys[j]]): + problems.append("顶层区重叠 %s×%s(%s ∩ %s)" % [akeys[i], akeys[j], arects[akeys[i]], arects[akeys[j]]]) + # 1920 档(Q4 注记对应面:单档 1280 闸+本两档断言钉 1920 构图恒等——分辨率维度不重复建闸) + get_window().size = Vector2i(1920, 1080) + for i in range(4): + await get_tree().process_frame + var vr2: Rect2 = get_viewport().get_visible_rect() + if vr2 != vr: + problems.append("1920 档逻辑视口漂移 %s ≠ %s(案甲:stretch 基座恒定)" % [vr2, vr]) + for k in azones: + var actl2: Control = azones[k] + if actl2 == null: + continue + var ar2 := Rect2(actl2.global_position, actl2.size) + if arects.has(k) and ar2 != arects[k]: + problems.append("1920 档 %s 矩形漂移 %s ≠ %s(案甲:构图与基准档一致)" % [k, ar2, arects[k]]) + if problems.is_empty(): + print("[layout_probe] PASS — 1280/1920 两档全过(基座1280x800/顶层三区互斥不越界/board_view含于D区/案甲构图恒定)") + get_tree().quit(0) + else: + for p in problems: + print("[layout_probe] FAIL — " + p) + print("[layout_probe] FAIL — %d 项断言未过" % problems.size()) + get_tree().quit(1) func _dump_ctrl(c: Control, depth: int) -> void: diff --git a/tools/probe_gate.sh b/tools/probe_gate.sh index 5b32d97c..228d49eb 100644 --- a/tools/probe_gate.sh +++ b/tools/probe_gate.sh @@ -5,17 +5,21 @@ # 段:ui_input_probe=默认必跑;ui_struct_probe=默认必跑(ui-struct-probe mini-loop 2026-10-05 注册—— # 全 UI 屏结构断言:按钮互压/等宽/出屏/零尺寸/标题屏双列);ui_contrast_probe=默认必跑(UI 设计对齐批 # T2 2026-10-06 注册——UI 文字对比度机检 ≥4.5:1 无区域例外,测点表随批维护);--click=加跑 ui_click_probe 行为走查 -# (自退出+退出码闸;超时多半=探针未适配新停点层,归 probe适配批);shot_probe 禁入本闸—— -# 必须窗口化运行(headless 无渲染面,探针内已有 fail-fast 护栏),归真人目检。 -# 用法: tools/probe_gate.sh [game目录] [--click] 缺省 game 目录=脚本所在仓库的 game/(worktree 内自动指向该 worktree) +# (自退出+退出码闸;超时多半=探针未适配新停点层,归 probe适配批)。 +# --full 段(迭代 19 卡① T3 收编 2026-10-08——重闸件):ui_probe/ui_tune_probe/t17_defer_probe/fx_probe +# 四件带断言探针收进(此前游离在一切闸外只能手敲);轻闸默认段断言面零删减零改动(--full 只增不改)。 +# 渲染面归 render_gate.sh 正主(窗口化:shot_probe 七屏+fx_shot/board_shot 取证+棋盘 golden 逐像素)—— +# 旧头注「shot_probe 禁入本闸……归真人目检」纪律句随三闸口径扩容改写退役(2026-10-07 用户授权=历史偷懒标记)。 +# 用法: tools/probe_gate.sh [game目录] [--click] [--full] 缺省 game 目录=脚本所在仓库的 game/(worktree 内自动指向该 worktree) # 依赖: Git Bash(coreutils timeout/taskkill) + PowerShell;Godot 路径可用环境变量 GODOT_BIN 覆盖。 set -u G="${GODOT_BIN:-/d/code/godot/Godot_v4.7.2-stable_win64_console.exe}" REPO="$(cd "$(dirname "$0")/.." && pwd)" -GAME=""; WITH_CLICK=0 +GAME=""; WITH_CLICK=0; WITH_FULL=0 for a in "$@"; do case "$a" in --click) WITH_CLICK=1 ;; + --full) WITH_FULL=1 ;; *) GAME="$a" ;; esac done @@ -85,6 +89,50 @@ if [ "$WITH_CLICK" -eq 1 ]; then fi fi +# ── --full 收编段(迭代 19 卡① T3 2026-10-08——四件带断言探针入闸;实测时长 ui_probe 17.4s/ +# ui_tune_probe 49.6s/t17_defer_probe 63.0s/fx_probe 21.9s,timeout 120=2-7×余量) ── +if [ "$WITH_FULL" -eq 1 ]; then + echo "======== ui_probe(--full 收编) ========" + timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/ui_probe.tscn >"$TMP/flp.log" 2>&1 + st=$?; kill_orphans + grep -E "\[ui_probe\]" "$TMP/flp.log" | tail -2 + if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then + echo "[probe_gate] FAIL — ui_probe 超时挂死(exit=$st),孤儿已清"; _rc=1 + elif ! grep -q "PASS" "$TMP/flp.log"; then + echo "[probe_gate] FAIL — ui_probe 未见 PASS(exit=$st)"; _rc=1 + fi + + echo "======== ui_tune_probe(--full 收编) ========" + timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/ui_tune_probe.tscn >"$TMP/flt.log" 2>&1 + st=$?; kill_orphans + grep -E "\[tune_probe\]" "$TMP/flt.log" | tail -2 + if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then + echo "[probe_gate] FAIL — ui_tune_probe 超时挂死(exit=$st),孤儿已清"; _rc=1 + elif ! grep -q "PASS" "$TMP/flt.log"; then + echo "[probe_gate] FAIL — ui_tune_probe 未见 PASS(exit=$st)"; _rc=1 + fi + + echo "======== t17_defer_probe(--full 收编) ========" + timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/t17_defer_probe.tscn >"$TMP/fld.log" 2>&1 + st=$?; kill_orphans + grep -E "\[t17_defer\]" "$TMP/fld.log" | tail -2 + if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then + echo "[probe_gate] FAIL — t17_defer_probe 超时挂死(exit=$st),孤儿已清"; _rc=1 + elif ! grep -q "PASS" "$TMP/fld.log"; then + echo "[probe_gate] FAIL — t17_defer_probe 未见 PASS(exit=$st)"; _rc=1 + fi + + echo "======== fx_probe(--full 收编) ========" + timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/fx_probe.tscn >"$TMP/flx.log" 2>&1 + st=$?; kill_orphans + grep -E "\[fx_probe\]" "$TMP/flx.log" | tail -2 + if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then + echo "[probe_gate] FAIL — fx_probe 超时挂死(exit=$st),孤儿已清"; _rc=1 + elif ! grep -q "PASS" "$TMP/flx.log"; then + echo "[probe_gate] FAIL — fx_probe 未见 PASS(exit=$st)"; _rc=1 + fi +fi + echo "======== 残留检查(批末闸) ========" left="$(orphans | wc -l | tr -d ' ')" total="$(tasklist 2>/dev/null | grep -ci '^Godot' || true)"