ui-struct-probe mini-loop T0-T6 全落(探针全屏零红+负向验证红+probe_gate 注册段全绿+冒烟772/0+run_all 14/15基线红+doc三闸零新增)——①M2双列叠放修复:ui_assembler.gd 右列挂回 HBox 第二列(原 pad.add_child(right) 误挂 MarginContainer 与左列同矩形叠放=实机目检抓到的根因;start_box/start_right 命名锚)②按钮宽度纪律:ui_kit.button(text,min_w=216) 全站最小宽度基准+SIZE_FILL(VBox 列内等宽拉满/HBox 行内不抢余宽——EXPAND_FILL 撑爆营地底条 1616px 实证教训注释在案);行板/角标/图标钮豁免9处(camp 5+shop 2+ap_bar 2+调试角标/诊断转储 2)③新增 ui_struct_probe(全 UI 屏结构断言:按钮互压/同容器等宽/出屏/零尺寸/标题屏双列五族;菜单簇真驱动+冒险簇真流+兑付屏 open 面合成喂入;ScrollContainer 内容豁免;modal 屏 queue_sort 手法坑在册)④probe_gate.sh 注册默认必跑段⑤07 改排:§⑪改排注记+§⑮分工注记+新增§⑯复验项⑥出口报告(代裁追认单 P1-P4+六轮 red-green 实录)=design/qa/ui-struct-probe-exit-report-2026-10-05.md
This commit is contained in:
1 parent
8944dca0d3
commit
d77a245bcc
12 files changed
+382
-18
No files matched your search
@@ -122,6 +122,7 @@ static func build_start_overlay(host: Control, db = null) -> Dictionary:
|
||||
cols.add_child(pad)
|
||||
overlay.add_child(cols)
|
||||
var box := VBoxContainer.new()
|
||||
box.name = "start_box"
|
||||
box.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
box.size_flags_stretch_ratio = 1.4
|
||||
box.add_theme_constant_override("separation", 10)
|
||||
@@ -156,17 +157,18 @@ static func build_start_overlay(host: Control, db = null) -> Dictionary:
|
||||
|
||||
# 迭代 14.5 调试整合批——调试入口收右下角标(与主按钮组非平级;DS_DEBUG=1 点亮,可见性归组合根)。
|
||||
# margin 16px 角距;字号降档与主按钮拉开层级
|
||||
var dbgbtn := UiKit.button("调试")
|
||||
var dbgbtn := UiKit.button("调试", 0.0) # 角标豁免宽度基准(0=不设 min/flags——角落小件)
|
||||
dbgbtn.add_theme_font_size_override("font_size", 13)
|
||||
dbgbtn.set_anchors_and_offsets_preset(Control.PRESET_BOTTOM_RIGHT, Control.PRESET_MODE_MINSIZE, 16)
|
||||
overlay.add_child(dbgbtn)
|
||||
|
||||
# 右列:立绘看台 + 玩法速览牌(M2——帮助全文保留表值驱动,仅容器化;内容含地形变异段不删)
|
||||
var right := VBoxContainer.new()
|
||||
right.name = "start_right"
|
||||
right.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
right.add_theme_constant_override("separation", 18)
|
||||
right.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
pad.add_child(right)
|
||||
cols.add_child(right) # ui-struct-probe 批 T2:右列挂 HBox 第二列(原误挂 pad 与左列同矩形叠放——M2 双列失效根因)
|
||||
var stage := VBoxContainer.new()
|
||||
stage.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
var foe_row := HBoxContainer.new()
|
||||
@@ -222,7 +224,7 @@ static func build_inter_overlay(host: Control) -> Dictionary:
|
||||
|
||||
# Playtest 诊断按钮(调试变体——battle.md §States 允许;卡死转储)
|
||||
static func build_diag_button(host: Control) -> Button:
|
||||
var diag_btn := UiKit.button("诊断转储")
|
||||
var diag_btn := UiKit.button("诊断转储", 0.0) # 角落小件豁免宽度基准(固定 offsets 112px 宽)
|
||||
diag_btn.set_anchors_preset(Control.PRESET_BOTTOM_LEFT)
|
||||
diag_btn.offset_left = 6.0
|
||||
diag_btn.offset_top = -30.0
|
||||
|
||||
@@ -85,7 +85,7 @@ func _init() -> void:
|
||||
row.add_child(_confirm_row)
|
||||
_confirm_lab = UiKit.label("RAP−%d · 旧板旗作废" % refresh_cost, 14, true, COL_INK)
|
||||
_confirm_row.add_child(_confirm_lab)
|
||||
var confirm_btn := UiKit.button("确认弃图")
|
||||
var confirm_btn := UiKit.button("确认弃图", 0.0) # HUD 顶条紧凑钮豁免宽度基准
|
||||
confirm_btn.pressed.connect(_disarm_and_fire)
|
||||
_confirm_row.add_child(confirm_btn)
|
||||
|
||||
@@ -99,7 +99,7 @@ func _init() -> void:
|
||||
refresh_box.add_theme_constant_override("separation", 0)
|
||||
refresh_box.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||
row.add_child(refresh_box)
|
||||
_btn_refresh = UiKit.button("弃图刷新")
|
||||
_btn_refresh = UiKit.button("弃图刷新", 0.0) # HUD 顶条紧凑钮豁免宽度基准
|
||||
_btn_refresh.pressed.connect(_on_refresh_pressed)
|
||||
refresh_box.add_child(_btn_refresh)
|
||||
_refresh_sub = UiKit.label("RAP−%d · 剩 0 次" % refresh_cost, 14, false, UiKit.INK_LABEL)
|
||||
|
||||
@@ -38,7 +38,7 @@ func _init() -> void:
|
||||
var top_sp := Control.new()
|
||||
top_sp.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
top.add_child(top_sp)
|
||||
_refresh_btn = UiKit.button("")
|
||||
_refresh_btn = UiKit.button("", 0.0) # 图标钮自设 44×44——豁免宽度基准
|
||||
_refresh_btn.icon = UiKit.load_tex(UiKit.TEX_ICON_REFRESH)
|
||||
_refresh_btn.custom_minimum_size = Vector2(44, 44)
|
||||
_refresh_btn.tooltip_text = "刷新候选——装配刷新件 X 优先,Y 为冒险池;不消耗 AP"
|
||||
|
||||
@@ -199,7 +199,7 @@ func _rebuild_tree(data: Dictionary) -> void:
|
||||
hb.add_child(name_l)
|
||||
if String(row.get("kind", "")) == "fork":
|
||||
continue # Fork 行去 UI 化——由 fork_groups 段承载切换钮
|
||||
var btn := UiKit.button("购买 %d" % int(row.get("price", 0)))
|
||||
var btn := UiKit.button("购买 %d" % int(row.get("price", 0)), 0.0) # 柜台行板紧凑钮豁免宽度基准
|
||||
btn.pressed.connect(func(): tree_buy_requested.emit(String(row.get("node_id", ""))))
|
||||
btn.disabled = not bool(row.get("can_buy", false))
|
||||
hb.add_child(btn)
|
||||
@@ -213,7 +213,7 @@ func _rebuild_tree(data: Dictionary) -> void:
|
||||
fb.add_child(gl)
|
||||
for side_row in (fg.get("sides", []) as Array):
|
||||
var sd := String(side_row.get("side", ""))
|
||||
var sb := UiKit.button(String(side_row.get("display_name", sd)))
|
||||
var sb := UiKit.button(String(side_row.get("display_name", sd)), 0.0) # 行板紧凑钮豁免
|
||||
sb.pressed.connect(func(): tree_fork_toggled.emit(group, sd))
|
||||
sb.disabled = not bool(side_row.get("owned", false))
|
||||
fb.add_child(sb)
|
||||
@@ -338,7 +338,7 @@ func _build_forge_row(holder: VBoxContainer, row: Dictionary) -> Dictionary:
|
||||
var price_l := UiKit.label("", 14, false, UiKit.STONE)
|
||||
price_l.custom_minimum_size = Vector2(64, 0)
|
||||
line.add_child(price_l)
|
||||
var btn := UiKit.button("锻造 +1 级")
|
||||
var btn := UiKit.button("锻造 +1 级", 0.0) # 柜台行板紧凑钮豁免宽度基准
|
||||
btn.pressed.connect(_emit_buy_requested.bind(attr))
|
||||
line.add_child(btn)
|
||||
holder.add_child(card)
|
||||
@@ -388,7 +388,7 @@ func _rebuild_attune(data: Dictionary) -> void:
|
||||
var form_l := UiKit.label("被动" if String(row.get("form", "")) == "passive" else "主动·充能", 12, false, UiKit.STONE)
|
||||
form_l.custom_minimum_size = Vector2(58, 0)
|
||||
line.add_child(form_l)
|
||||
var btn := UiKit.button("装配")
|
||||
var btn := UiKit.button("装配", 0.0) # 行板紧凑钮豁免
|
||||
btn.pressed.connect(attune_toggled.emit.bind(iid))
|
||||
line.add_child(btn)
|
||||
_workshop_holder.add_child(line)
|
||||
@@ -398,7 +398,7 @@ func _rebuild_attune(data: Dictionary) -> void:
|
||||
refresh_line.add_theme_constant_override("separation", 6)
|
||||
_refresh_l = UiKit.label("", 12, false, UiKit.STONE)
|
||||
refresh_line.add_child(_refresh_l)
|
||||
_refresh_btn = UiKit.button("升级")
|
||||
_refresh_btn = UiKit.button("升级", 0.0) # 行板紧凑钮豁免
|
||||
_refresh_btn.pressed.connect(_emit_buy_requested.bind("attune_refresh"))
|
||||
refresh_line.add_child(_refresh_btn)
|
||||
_workshop_holder.add_child(refresh_line)
|
||||
|
||||
@@ -38,7 +38,7 @@ func _init() -> void:
|
||||
var top_sp := Control.new()
|
||||
top_sp.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
top.add_child(top_sp)
|
||||
_refresh_btn = UiKit.button("")
|
||||
_refresh_btn = UiKit.button("", 0.0) # 图标钮自设 44×44——豁免宽度基准
|
||||
_refresh_btn.icon = UiKit.load_tex(UiKit.TEX_ICON_REFRESH)
|
||||
_refresh_btn.custom_minimum_size = Vector2(44, 44)
|
||||
_refresh_btn.disabled = true # MC-1 投喂后启用(set_refresh_display)
|
||||
@@ -106,7 +106,7 @@ func _build_card(p: Dictionary) -> Dictionary:
|
||||
row.add_child(price_l)
|
||||
var bought_l := UiKit.label("", 14, false, UiKit.WARM_FAINT)
|
||||
row.add_child(bought_l)
|
||||
var btn := UiKit.button("购买")
|
||||
var btn := UiKit.button("购买", 0.0) # 行卡紧凑钮豁免宽度基准
|
||||
btn.pressed.connect(_emit_buy_requested.bind(p))
|
||||
row.add_child(btn)
|
||||
_cards_box.add_child(card)
|
||||
|
||||
@@ -253,9 +253,14 @@ static func scaled(base: int, pct: int) -> int:
|
||||
return maxi(1, int(round(float(base) * (100.0 + float(pct)) / 100.0)))
|
||||
|
||||
|
||||
static func button(text: String) -> Button:
|
||||
# 宽度纪律(ui-struct-probe 批 T3——全站按钮最小宽度基准 216+SIZE_FILL:VBox 列内拉满列宽等宽,
|
||||
# HBox 行内不抢余宽(EXPAND_FILL 会把行撑爆——营地底条 1616px 实证);角落小件/紧凑位传 0 豁免)
|
||||
static func button(text: String, min_w := 216.0) -> Button:
|
||||
var b := Button.new()
|
||||
b.text = text
|
||||
if min_w > 0.0:
|
||||
b.custom_minimum_size = Vector2(min_w, 0)
|
||||
b.size_flags_horizontal = Control.SIZE_FILL
|
||||
_apply_button_skin(b)
|
||||
return b
|
||||
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# UI 结构探针(ui-struct-probe mini-loop 2026-10-05——计划书 .zcode/plans/ui-struct-probe-miniloop-plan-2026-10-05.md T0/T1)
|
||||
# 全 UI 屏结构断言一次跑完:逐屏实例化(菜单簇真驱动 + 冒险簇真流推进 + 兑付屏真实 open 面合成喂入),
|
||||
# 断言族:
|
||||
# ①overlap 同屏内可见未禁用按钮两两 global_rect 不相交(互压=红;层间模态盖底层系设计内——各屏采样以屏根为界天然排除)
|
||||
# ②equal_width 同一直接父容器内 ≥2 可见按钮宽度差 >1px=红(容器纪律:组内等宽)
|
||||
# ③offscreen 可见件 global_rect 完全落在视口外=红
|
||||
# ④zero_size 可见件宽或高 ≤0=红
|
||||
# ⑤start 专项 标题屏左列(start_box)与右列(start_right)矩形不相交(M2 双列契约——叠放缺陷回归闸)
|
||||
# 豁免:WL 常量表按「屏名/规则名/件名」登记合法例外(登记须注理由于行尾注释)。
|
||||
# 运行:Godot_console.exe --headless --path . res://smoke/ui_struct_probe.tscn
|
||||
extends Node
|
||||
|
||||
const ProbeUiUtil := preload("res://smoke/probe_ui_util.gd")
|
||||
|
||||
# 豁免清单(空 = 零豁免起步;红灯逐条核对后合法者入表并注理由)
|
||||
const WL := {}
|
||||
|
||||
var problems: Array = []
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
await get_tree().process_frame # autoload 就绪
|
||||
var main: Node = load("res://presentation/main.tscn").instantiate()
|
||||
add_child(main)
|
||||
await get_tree().process_frame
|
||||
|
||||
# ---- 菜单簇(开始层可见态;逐屏开→采样→关,模态恢复走组合根真实路径)----
|
||||
ProbeUiUtil.isolate_save(main, "smoke_ui_struct_probe_menu", true, false, true)
|
||||
_settle()
|
||||
await _audit("start", [main._start_overlay])
|
||||
_start_columns_contract(main)
|
||||
|
||||
main._open_settings()
|
||||
_settle()
|
||||
await _audit("settings", [main.settings_overlay])
|
||||
main._on_settings_closed()
|
||||
_settle()
|
||||
|
||||
var campbtn: Button = ProbeUiUtil.find_button(main._start_overlay, "营地", "prefix")
|
||||
if campbtn != null:
|
||||
campbtn.pressed.emit()
|
||||
_settle()
|
||||
await _audit("camp", [main.camp_overlay])
|
||||
main._on_camp_closed()
|
||||
_settle()
|
||||
else:
|
||||
problems.append("[camp] 营地入口按钮缺失(驱动中断)")
|
||||
|
||||
main.confirm_dialog.open("结构探针采样", "确认")
|
||||
_settle()
|
||||
await _audit("confirm", [main.confirm_dialog])
|
||||
main.confirm_dialog.close()
|
||||
_settle()
|
||||
|
||||
main._show_debug_overlay()
|
||||
_settle()
|
||||
await _audit("debug", [main.debug_overlay])
|
||||
main._close_debug_overlay()
|
||||
_settle()
|
||||
|
||||
# ---- 冒险簇(真流推进:开冒险→地图选路态→选战斗节点→战斗 HUD)----
|
||||
ProbeUiUtil.isolate_save(main, "smoke_ui_struct_probe_adv", true, false, true)
|
||||
main._start_adventure_from_menu()
|
||||
_settle(4)
|
||||
await _audit("level_map", [main.level_screen])
|
||||
|
||||
var battle_id := _pick_battle_node(main)
|
||||
if battle_id >= 0:
|
||||
main.level_screen.route_chosen.emit(battle_id)
|
||||
_settle(6)
|
||||
_audit("battle_hud", [main.board_zone, main.info_panel, main.stage])
|
||||
else:
|
||||
problems.append("[battle_hud] 候选中无战斗节点(驱动中断——候选=%s)" % str(main.level_screen._cand_ids))
|
||||
|
||||
# ---- 兑付屏(真实 open 面 + 合成最小数据——结构断言只看矩形/宽度/可见性)----
|
||||
main._end_overlay.open_victory("结构探针·胜利演算", "返回", Rect2(200, 200, 400, 300), "GP 0")
|
||||
_settle()
|
||||
await _audit("battle_end", [main._end_overlay])
|
||||
main._end_overlay.close()
|
||||
_settle()
|
||||
|
||||
main.bonus_overlay.open_pick("结构探针·三选一", [
|
||||
{"id": "probe_heal", "text": "回血\n恢复 3 点"},
|
||||
{"id": "probe_magic", "text": "爆裂轰击\n下一击 +5"},
|
||||
{"id": "probe_pierce", "text": "穿甲\n无视护甲"},
|
||||
])
|
||||
_settle()
|
||||
await _audit("bonus_pick", [main.bonus_overlay])
|
||||
main.bonus_overlay.visible = false
|
||||
_settle()
|
||||
|
||||
main.shop_overlay.open_shop([
|
||||
{"product_id": "probe_p1", "display_name": "探针商品甲", "attr": "atk", "flat_value": 1, "price": 10, "purchased": 0},
|
||||
{"product_id": "probe_p2", "display_name": "探针商品乙", "attr": "hp", "flat_value": 2, "price": 20, "purchased": 0},
|
||||
], 100)
|
||||
_settle()
|
||||
await _audit("shop", [main.shop_overlay])
|
||||
|
||||
# ---- 判定 ----
|
||||
if problems.is_empty():
|
||||
print("[ui_struct_probe] PASS — 全屏结构断言零红")
|
||||
get_tree().quit(0)
|
||||
else:
|
||||
for p in problems:
|
||||
print("[ui_struct_probe] FAIL — %s" % p)
|
||||
get_tree().quit(1)
|
||||
|
||||
|
||||
func _pick_battle_node(main: Node) -> int:
|
||||
# 入口/兑付停点走 level_screen.open_choices 直传(不写 _pending_candidates)——
|
||||
# 候选从视图态取:_cand_ids × runner.nodes() 的 type(编号解析=node_id_of 单一出口)
|
||||
var ls: Node = main.level_screen
|
||||
if ls._runner == null:
|
||||
return -1
|
||||
for nd in ls._runner.nodes():
|
||||
if String(nd.get("type", "")) in ["battle", "boss"]:
|
||||
var nid: int = ls._runner.node_id_of(nd)
|
||||
if nid in ls._cand_ids:
|
||||
return nid
|
||||
return -1
|
||||
|
||||
|
||||
func _settle(frames := 4) -> void:
|
||||
for i in range(frames):
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame # 布局排序系帧尾 call_deferred——末帧后再保一帧
|
||||
|
||||
|
||||
# ---- 采样与断言 ----
|
||||
|
||||
func _visible_children(root: Control) -> Array:
|
||||
var out: Array = []
|
||||
var stack: Array = [root]
|
||||
while not stack.is_empty():
|
||||
var c: Control = stack.pop_back()
|
||||
if c == null:
|
||||
continue
|
||||
if c.is_visible_in_tree():
|
||||
out.append(c)
|
||||
for ch in c.get_children():
|
||||
if ch is Control:
|
||||
stack.append(ch)
|
||||
return out
|
||||
|
||||
|
||||
func _buttons(root: Control) -> Array:
|
||||
var out: Array = []
|
||||
for c in _visible_children(root):
|
||||
if c is Button and not c.disabled:
|
||||
out.append(c)
|
||||
return out
|
||||
|
||||
|
||||
func _wl(screen: String, rule: String, ctl: Control) -> bool:
|
||||
var s: Dictionary = WL.get(screen, {})
|
||||
if s.is_empty():
|
||||
return false
|
||||
var names: Array = s.get(rule, [])
|
||||
if names.is_empty():
|
||||
return false
|
||||
return String(ctl.name) in names
|
||||
|
||||
|
||||
func _force_layout(root: Control) -> void:
|
||||
# modal 屏首次 visible 后容器排序不自动传播(size 已算而 position 停 0——4.7 行为)——
|
||||
# 采样前对子树全部 Container 强制 queue_sort
|
||||
var stack: Array = [root]
|
||||
while not stack.is_empty():
|
||||
var c: Control = stack.pop_back()
|
||||
if c is Container:
|
||||
c.queue_sort()
|
||||
for ch in c.get_children():
|
||||
if ch is Control:
|
||||
stack.append(ch)
|
||||
|
||||
|
||||
func _in_scroll(c: Control) -> bool:
|
||||
var p := c.get_parent()
|
||||
while p is Control:
|
||||
if p is ScrollContainer:
|
||||
return true
|
||||
p = p.get_parent()
|
||||
return false
|
||||
|
||||
|
||||
func _audit(screen: String, roots: Array) -> void:
|
||||
for r in roots:
|
||||
if r != null and is_instance_valid(r):
|
||||
_force_layout(r)
|
||||
await _settle(1)
|
||||
if OS.get_environment("STRUCT_PROBE_DEBUG") == "1":
|
||||
for r in roots:
|
||||
if r != null and is_instance_valid(r):
|
||||
_dump(r, 0)
|
||||
var all_ctls: Array = []
|
||||
var all_buttons: Array = []
|
||||
for r in roots:
|
||||
if r == null or not is_instance_valid(r):
|
||||
problems.append("[%s] 采样根缺失" % screen)
|
||||
return
|
||||
all_ctls += _visible_children(r)
|
||||
all_buttons += _buttons(r)
|
||||
var vp := get_viewport().get_visible_rect()
|
||||
# ①overlap:操作钮两两不相交
|
||||
for i in range(all_buttons.size()):
|
||||
for j in range(i + 1, all_buttons.size()):
|
||||
var a: Control = all_buttons[i]
|
||||
var b: Control = all_buttons[j]
|
||||
if _wl(screen, "overlap", a) or _wl(screen, "overlap", b):
|
||||
continue
|
||||
if a.get_global_rect().intersects(b.get_global_rect()):
|
||||
problems.append("[%s][overlap] %s 与 %s 矩形相交(%s ∩ %s)" % [
|
||||
screen, a.name, b.name, a.get_global_rect(), b.get_global_rect()])
|
||||
# ②equal_width:同一直接父容器内 ≥2 可见按钮宽度差 >1px
|
||||
var groups := {}
|
||||
for b in all_buttons:
|
||||
var k: Node = b.get_parent()
|
||||
if not groups.has(k):
|
||||
groups[k] = []
|
||||
groups[k].append(b)
|
||||
for k in groups:
|
||||
var g: Array = groups[k]
|
||||
if g.size() < 2:
|
||||
continue
|
||||
var w0: float = (g[0] as Control).size.x
|
||||
var diff := false
|
||||
for b in g:
|
||||
if _wl(screen, "equal_width", b):
|
||||
diff = false
|
||||
break
|
||||
if absf((b as Control).size.x - w0) > 1.0:
|
||||
diff = true
|
||||
if diff:
|
||||
var names: Array = []
|
||||
var widths: Array = []
|
||||
for x in g:
|
||||
names.append(String((x as Control).name))
|
||||
widths.append((x as Control).size.x)
|
||||
problems.append("[%s][equal_width] 同容器按钮宽度不一(%s——%s)" % [screen, str(names), str(widths)])
|
||||
# ③offscreen + ④zero_size(ScrollContainer 内容坐标系超出=可滚动设计内——豁免)
|
||||
for c in all_ctls:
|
||||
if _in_scroll(c):
|
||||
continue
|
||||
var gr: Rect2 = c.get_global_rect()
|
||||
if not _wl(screen, "offscreen", c) and not gr.intersects(vp):
|
||||
problems.append("[%s][offscreen] %s 完全出视口(%s)" % [screen, c.name, gr])
|
||||
if not _wl(screen, "zero_size", c) and (c.size.x <= 0.0 or c.size.y <= 0.0):
|
||||
problems.append("[%s][zero_size] %s 零尺寸(%s)" % [screen, c.name, c.size])
|
||||
|
||||
|
||||
func _dump(c: Control, depth: int) -> void:
|
||||
var ind := " ".repeat(depth)
|
||||
print("[dump] %s%s(%s) pos=%s gpos=%s size=%s vis=%s" % [ind, c.name, c.get_class(),
|
||||
c.position, c.get_global_position(), c.size, c.is_visible_in_tree()])
|
||||
for ch in c.get_children():
|
||||
if ch is Control:
|
||||
_dump(ch, depth + 1)
|
||||
|
||||
|
||||
# ⑤start 专项:左列/右列矩形不相交(M2 双列契约——装配件命名 start_box/start_right 为探针锚点)
|
||||
func _start_columns_contract(main: Node) -> void:
|
||||
var box: Control = main._start_overlay.find_child("start_box", true, false)
|
||||
var right: Control = main._start_overlay.find_child("start_right", true, false)
|
||||
if box == null or right == null:
|
||||
problems.append("[start][columns] 左列/右列节点缺失(start_box/start_right——装配命名失守)")
|
||||
return
|
||||
if box.get_global_rect().intersects(right.get_global_rect()):
|
||||
problems.append("[start][columns] 左列 %s 与右列 %s 相交(双列叠放回归——M2 缺陷闸)" % [
|
||||
box.get_global_rect(), right.get_global_rect()])
|
||||
@@ -0,0 +1 @@
|
||||
uid://dqaphrds5ncet
|
||||
@@ -0,0 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://smoke/ui_struct_probe.gd" id="1"]
|
||||
|
||||
[node name="UIStructProbe" type="Node"]
|
||||
script = ExtResource("1")
|
||||
Reference in new issue
Block a user