101 lines
4.3 KiB
GDScript
101 lines
4.3 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# 整屏截图探针(迭代 9 卡⑰a 2026-09-30——呈现层测试分级 B 档基建:四屏视口截图程序化产出)。
|
||
# 必须**窗口化**运行(真渲染设备——headless 无渲染面,get_image 得到空纹理):
|
||
# Godot_console.exe --path . res://smoke/shot_probe.tscn -- <输出目录>
|
||
# 流程 = 真实按钮信号路径驱动(ui_click_probe 同款;存档/设置/元进度三面换注隔离档——禁碰真实档,
|
||
# 坑册「断点写盘随 save_manager 引用走」教训同款覆盖):菜单(启动态)→ 营地(campbtn)→
|
||
# 关卡画面(lvbtn→停点出图)→ 战斗(level_screen.press_candidate(0) 真实选路提交)。
|
||
# 每屏双帧 + frame_post_draw 同步后抓 get_viewport().get_texture(),PNG 落输出目录;
|
||
# 最小自检 = 四件存在且非零字节(像素方差断言归 tools/shot_checker.py——B 档骨架分工)。
|
||
extends Node
|
||
|
||
var main
|
||
var out_dir := ""
|
||
var _problems: Array = []
|
||
|
||
|
||
func _ready() -> void:
|
||
var args := OS.get_cmdline_user_args()
|
||
out_dir = args[0] if args.size() >= 1 and args[0] != "" else "user://qa-screens"
|
||
DirAccess.make_dir_recursive_absolute(out_dir)
|
||
await get_tree().process_frame
|
||
main = load("res://presentation/main.tscn").instantiate()
|
||
add_child(main)
|
||
await get_tree().process_frame
|
||
# ── 存档/元进度/设置三面隔离(ui_click_probe 同款——真档零污染) ──
|
||
for p0 in ["user://smoke_shot_probe.json", "user://smoke_shot_probe.json.tmp",
|
||
"user://smoke_shot_probe_settings.cfg"]:
|
||
if FileAccess.file_exists(p0):
|
||
DirAccess.remove_absolute(p0)
|
||
var iso_c = load("res://core/save_manager.gd").new("user://smoke_shot_probe.json")
|
||
iso_c.load_from_disk()
|
||
main.save_manager = iso_c
|
||
main.meta = load("res://core/meta_progression.gd").new(main.db, iso_c)
|
||
main.settings_store = load("res://presentation/settings_store.gd").new("user://smoke_shot_probe_settings.cfg")
|
||
await get_tree().process_frame
|
||
# ① 菜单(启动态)
|
||
await _snap("menu")
|
||
# ② 营地
|
||
var campbtn: Button = _find_button_by_prefix(main._start_overlay, "营地")
|
||
if campbtn == null:
|
||
_problems.append("营地入口缺失(开始层 campbtn)")
|
||
else:
|
||
campbtn.pressed.emit()
|
||
await get_tree().process_frame
|
||
await _snap("camp")
|
||
var close_btn: Button = _find_button_by_prefix(main.camp_overlay, "关闭营地")
|
||
if close_btn != null:
|
||
close_btn.pressed.emit()
|
||
await get_tree().process_frame
|
||
# ③ 关卡画面(开始冒险→停点出图——冒险开始即出图,停点前移批语义)
|
||
var lvbtn: Button = _find_button_by_prefix(main._start_overlay, "开始冒险")
|
||
if lvbtn == null:
|
||
_problems.append("开始冒险按钮缺失(开始层 lvbtn)")
|
||
else:
|
||
lvbtn.pressed.emit()
|
||
for i in 4:
|
||
await get_tree().process_frame
|
||
await _snap("level")
|
||
# ④ 战斗(节点一——真实选路提交路径 press_candidate,ui_click_probe :199 同款)
|
||
if main.level_screen != null and main.level_screen.visible:
|
||
main.level_screen.press_candidate(0)
|
||
for i in 10:
|
||
await get_tree().process_frame
|
||
await _snap("battle")
|
||
else:
|
||
_problems.append("关卡画面未出现(lvbtn 路径)")
|
||
# ── 最小自检:四件存在且非零字节 ──
|
||
for s in ["menu", "camp", "level", "battle"]:
|
||
var p := out_dir.path_join("shot_%s.png" % s)
|
||
if not FileAccess.file_exists(p):
|
||
_problems.append("截图缺失: %s" % p)
|
||
else:
|
||
var fa := FileAccess.open(p, FileAccess.READ)
|
||
if fa == null or fa.get_length() < 1000:
|
||
_problems.append("截图疑似空文件: %s" % p)
|
||
print("[shot_probe] 完成 out=%s 问题=%d %s" % [out_dir, _problems.size(), str(_problems)])
|
||
get_tree().quit(1 if _problems.size() > 0 else 0)
|
||
|
||
|
||
func _snap(key: String) -> void:
|
||
await RenderingServer.frame_post_draw
|
||
await RenderingServer.frame_post_draw
|
||
var img: Image = get_viewport().get_texture().get_image()
|
||
if img == null:
|
||
_problems.append("视口纹理为空: %s" % key)
|
||
return
|
||
img.save_png(out_dir.path_join("shot_%s.png" % key))
|
||
print("[shot_probe] saved %s (%dx%d)" % [key, img.get_width(), img.get_height()])
|
||
|
||
|
||
func _find_button_by_prefix(root: Node, prefix: String) -> Button:
|
||
if root == null:
|
||
return null
|
||
if root is Button and String(root.text).contains(prefix):
|
||
return root
|
||
for c in root.get_children():
|
||
var r := _find_button_by_prefix(c, prefix)
|
||
if r != null:
|
||
return r
|
||
return null
|