feat(布局闸): layout_probe 补回分辨率几何断言(两档·一份断言函数据)
背景:孤儿探针 ui_layout_probe 退役后1920 档布局几何无机器承载。重建但改形态——
「档位表(数据)+ 一份不变量断言(代码)+ 循环代入」,测试代码量与档数无关,
增删档位只改 VIEWPORTS 数组一行。这与业界 Playwright parametrize/projects 同构:
同一个 test body 跑多档,变的只是输入表与基线图张数(数据非代码)。
档位按 battle.md §验收现登记的两档:基座 1280×800、16:9 全屏 1920×1080。不新增档。
关键修正——旧探针那条「1920 档几何恒定」断言是假的:
设get_window().size = 1920×1080 → 逻辑视口仍是 1280×800(headless 下不生效)
设 content_scale_size = 1422×800 → 逻辑视口 = 1422×800(真生效)
Godot 文档指定的运行时改基座接口是 content_scale_size;旧探针用错了 API,
其断言永远为真、当年报 PASS 是假绿。本探针改用正确接口。
断言面(每档跑一遍):三区非零/不越视口/两两不重叠/board_view ⊆ board_zone/
格子边长 ≥36px(battle.md §棋盘格尺寸公式 1280 档 12×12 → 36px)。
变异验证(CLAUDE.md 第9条)两发:
① battle_stage_view B区 252→700 → 5 条红(两档都中)✓ 还原后 PASS
② 注入全幅 ColorRect → 未见红 ✗ —— 判据只点名三区不覆盖第四个兄弟,
该缺陷类不在拦截面内。已在代码注释标明边界,未过度设计补递归遍历
(递归全子树查重叠极易误报容器父子关系)。
挂 probe_gate 默认必跑段(轻闸即跑)。S10 文件头闸本批零新增。
This commit is contained in:
1 parent
f22adb5564
commit
5af6c8a8b4
3 files changed
+114
No files matched your search
@@ -0,0 +1,94 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 布局几何断言探针(正主=tools/probe_gate.sh 默认段;呈现层三区布局的分辨率维度验收)
|
||||
# 用途:battle.md §验收「1280×800 与 16:9 全屏下三区无遮挡/无重叠/无裁切」的可执行化
|
||||
# GDD 锚:design/gdd/ux/battle.md §Layout Zones(B/C/D 三区几何)
|
||||
# 硬约束:档位表是数据、断言是一份函数据——加档只改表不动断言(禁每档建一个探针)
|
||||
extends Node
|
||||
|
||||
const ProbeUiUtil := preload("res://smoke/probe_ui_util.gd") # 隔离样板单点
|
||||
|
||||
# 分辨率档位表(逻辑视口尺寸;数据侧——增删档位只改这里,断言代码零改动)
|
||||
const VIEWPORTS := [
|
||||
{"label": "基座 1280x800", "size": Vector2i(1280, 800)},
|
||||
{"label": "16:9 全屏 1920x1080", "size": Vector2i(1920, 1080)},
|
||||
]
|
||||
const MIN_CELL := 36.0 # 格子可读门槛(battle.md §棋盘格尺寸公式 1280 档 12×12 → 36px)
|
||||
|
||||
var _problems: PackedStringArray = []
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
var main: Node = load("res://presentation/main.tscn").instantiate()
|
||||
add_child(main)
|
||||
await get_tree().process_frame
|
||||
ProbeUiUtil.isolate_save(main, "smoke_layout_probe", true, false, true)
|
||||
main.debug_overlay.seed_edit.text = "42" # 种子钉死(确定性禁依赖默认随机分支)
|
||||
main._start_adventure_from_menu()
|
||||
for i in range(8):
|
||||
await get_tree().process_frame
|
||||
# 进战斗屏——三区(B 战斗画面/C 角色信息/D 扫雷图)同屏常驻,菜单屏不构成断言面
|
||||
main.level_screen.press_candidate(0)
|
||||
for i in range(20):
|
||||
await get_tree().process_frame
|
||||
for vp in VIEWPORTS:
|
||||
# content_scale_size 是headless 下改逻辑视口的唯一有效入口(get_window().size
|
||||
# 在 headless 下不改变视口——用旧 API 会得到永远为真的假断言)
|
||||
get_tree().root.content_scale_size = vp["size"]
|
||||
for i in range(5):
|
||||
await get_tree().process_frame
|
||||
_check_one(vp["label"], main)
|
||||
# 复原本项目基座(探针不该把进程留在末档尺寸)
|
||||
get_tree().root.content_scale_size = Vector2i(1280, 800)
|
||||
if _problems.is_empty():
|
||||
print("[layout_probe] PASS — %d 档几何不变量全过(三区非零/不越视口/互不重叠/view 含于 D 区/格子 ≥%.0fpx)"
|
||||
% [VIEWPORTS.size(), MIN_CELL])
|
||||
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 _check_one(label: String, main: Node) -> void:
|
||||
var vr: Rect2 = get_viewport().get_visible_rect()
|
||||
if vr.size != Vector2(VIEWPORTS[0]["size"]) and vr.size.y < 1.0:
|
||||
_problems.append("[%s] 逻辑视口退化 %s" % [label, vr.size])
|
||||
var rects := {}
|
||||
for key in ["stage", "info_panel", "board_zone"]:
|
||||
var ctl: Control = main.get(key)
|
||||
if ctl == null:
|
||||
_problems.append("[%s] %s = null(三区未装配)" % [label, key])
|
||||
return
|
||||
var r := Rect2(ctl.global_position, ctl.size)
|
||||
rects[key] = r
|
||||
if r.size.x <= 0.0 or r.size.y <= 0.0:
|
||||
_problems.append("[%s] %s 零尺寸 %s" % [label, key, r.size])
|
||||
elif not vr.encloses(r):
|
||||
_problems.append("[%s] %s 越视口 %s ⊄ %s" % [label, key, r, vr])
|
||||
# 判据覆盖边界(如实记录,勿当已拦截):本探针按名点名三区做几何判定,
|
||||
# 不遍历战斗屏宿主的全部后代。变异验证第二发证——向装配器注入一个全幅 ColorRect
|
||||
# (挂在 battle_root 的内层根下)时探针仍全绿:「未登记兄弟控件」这一缺陷类不在本探针
|
||||
# 拦截面内。要拦它得改为递归遍历全子树找重叠,那是另一套判据(且极易误报容器父子关系),
|
||||
# 归后续批按需立,不在本批范围。
|
||||
var bv: Control = main.board_view
|
||||
if bv == null:
|
||||
_problems.append("[%s] board_view = null" % label)
|
||||
return
|
||||
var br := Rect2(bv.global_position, bv.size)
|
||||
if not vr.encloses(br):
|
||||
_problems.append("[%s] board_view 越视口 %s ⊄ %s" % [label, br, vr])
|
||||
if rects.has("board_zone") and not rects["board_zone"].encloses(br):
|
||||
_problems.append("[%s] board_view 越出 board_zone(%s ⊄ %s)"
|
||||
% [label, br, rects["board_zone"]])
|
||||
var keys := rects.keys()
|
||||
for i in range(keys.size()):
|
||||
for j in range(i + 1, keys.size()):
|
||||
if rects[keys[i]].intersects(rects[keys[j]]):
|
||||
_problems.append("[%s] 顶层区重叠 %s×%s(%s ∩ %s)"
|
||||
% [label, keys[i], keys[j], rects[keys[i]], rects[keys[j]]])
|
||||
var cell: float = main.board_view.get("cell_size")
|
||||
if cell < MIN_CELL:
|
||||
_problems.append("[%s] 格子边长 %.1fpx < 可读门槛 %.0fpx" % [label, cell, MIN_CELL])
|
||||
@@ -0,0 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://clayoutprobe01"]
|
||||
|
||||
[ext_resource type="Script" path="res://smoke/layout_probe.gd" id="1"]
|
||||
|
||||
[node name="LayoutProbe" type="Node"]
|
||||
script = ExtResource("1")
|
||||
@@ -6,6 +6,10 @@
|
||||
# 全 UI 屏结构断言:按钮互压/等宽/出屏/零尺寸/标题屏双列);ui_contrast_probe=默认必跑(UI 设计对齐批
|
||||
# T2 2026-10-06 注册——UI 文字对比度机检 ≥4.5:1 无区域例外,测点表随批维护);--click=加跑 ui_click_probe 行为走查
|
||||
# (自退出+退出码闸;超时多半=探针未适配新停点层,归 probe适配批)。
|
||||
# layout_probe=默认必跑(2026-10-15 建——分辨率几何断言,档位表是数据/断言是一份函数,
|
||||
# 增删档位只改表内一行;用 content_scale_size 改逻辑视口,headless 下真生效——
|
||||
# 旧 ui_layout_probe 用 get_window().size 在 headless 下不改变视口,其「1920 档恒定」
|
||||
# 断言是永远为真的假断言,随该探针退役)。
|
||||
# --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 逐像素)——
|
||||
@@ -67,6 +71,16 @@ elif ! grep -q "PASS" "$TMP/stp.log"; then
|
||||
echo "[probe_gate] FAIL — ui_struct_probe 未见 PASS(exit=$st)"; _rc=1
|
||||
fi
|
||||
|
||||
echo "======== layout_probe(分辨率几何·两档) ========"
|
||||
timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/layout_probe.tscn >"$TMP/lay.log" 2>&1
|
||||
st=$?; kill_orphans
|
||||
grep -E "PASS|FAIL" "$TMP/lay.log" | head -12
|
||||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||||
echo "[probe_gate] FAIL — layout_probe 超时挂死(exit=$st),孤儿已清"; _rc=1
|
||||
elif ! grep -q "PASS" "$TMP/lay.log"; then
|
||||
echo "[probe_gate] FAIL — layout_probe 未见 PASS(exit=$st)"; _rc=1
|
||||
fi
|
||||
|
||||
echo "======== ui_contrast_probe ========"
|
||||
timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/ui_contrast_probe.tscn >"$TMP/ctp.log" 2>&1
|
||||
st=$?; kill_orphans
|
||||
|
||||
Reference in new issue
Block a user