补提交(跨会话旧报告欠账+uid边车): 四份在册引用而未跟踪的过程件入库——盘点-验证与清单体系-2026-10-28(接收方=待办清单『验证与清单体系整改』条目,阶段0盘点明细)+审计-全库架构审查-2026-10-29(接收方=待办清单『架构审查优化讨论』条目,七节读数)+工程改动报告-存量断言精简C档-2026-10-28(接收方=同条目阶段2闭合记录,6组33→7+冻结清单对账1587→1561)+评估-美术资产清单与性能预算-2026-10-30(接收方=待办清单卡④条目『评估报告同步』句,美术师交付物两章合并评估,未改权威文档);tune_sim_batch_parity_test.gd.uid 边车补齐(iter44卡③对拍套件.gd 在库而.uid漏提交——.gd/.uid同步纪律)

This commit is contained in:
DKChan committed 2026-10-31 05:01:33 +08:00
1 parent 21c4a5e915
commit f76c878e4f
5 files changed
+574

No files matched your search

@@ -0,0 +1 @@
uid://ceh2mcde5wrtj
@@ -0,0 +1,208 @@
# 审计:全库架构审查(2026-10-29)
> **性质**:设计讨论段过程稿(审计读数)。`game/` 全目录只读架构审查的发现清单与证据,作为写卡段、工程改动批与文档设计批的输入材料;结论被吸收进权威文档与修复批后按「吸收即死」退役。
> **范围**:core 44 件、presentation 71 件、data 24 schema + 19 表、sim 15 件、probe 19 件、tests 69 件、`tools/` 闸件。判据双轨=项目自有规范(`design/engineering/architecture/architecture.md` v1.24 + 五份工程规约)+ Godot 4.7 最佳实践。
> **方式**:分层依赖方向/数据管线/测试覆盖三路并行静态取证+机械 grep 复核。**零测试执行、零引擎运行、零闸执行**(测试两入口铁规),全部结论来自静态读盘;下文每条命中均给 文件:行号,抽查原文已核。
---
## 一、总评
分层骨架是健康的:core 纯逻辑(44 件里 37 件 `RefCounted`、3 件 `Resource`,`extends Node` 仅 4 个 Autoload)、Core 零 UI 八项判据全零命中、core 内 RNG 单点 100% 成立、字符串式信号连接全库 0 处、core 层函数体超 120 行零命中、19 表 ↔ 19 schema 一一对应零孤儿、无循环 preload。
**真正的风险不在分层,在三条写了却没闸守的纪律被实际破坏,外加三处守卫面缺口。**
1. 「玩法数值只存 `data/tables/`」被 18 处硬编码击穿,其中两处是玩家可见的提示文案数字。
2. `game/sim/` 与 `game/core/` 存在四组平行实现,直接威胁「模拟读数 = 游戏真实数值」这一前提。
3. RNG 单点、类型闸、质量闸、结算禁 await 的扫描面都止步于 core/presentation(部分只扫 9 文件显式清单),sim / probe / tests 三层几乎不受约束。
另有封装面(结算段类直取编排器私有成员)与文档漂移(架构文档登记件与盘上实件不一致)两类低危问题。
---
## 二、健康面(已逐条核,零问题)
| 判据 | 结果 | 证据口径 |
|---|---|---|
| Core 零 UI | 零命中 | `res://presentation`、`Control`/`Node2D` 系、`.tscn`、`$` 节点语法、`get_parent()` 在 core 全域零命中;core 内 `get_node` 仅 2 处且均为 Autoload 取依赖 |
| RNG 单点(core 内) | 100% 合规 | 15 处 `open_stream` + 1 处 `pure_stream` 全经 RngService;`RandomNumberGenerator.new()` 在 core 内仅出现在 `rng_service.gd` 自身 |
| 字符串式信号连接 | 全库 0 处 | 正则 `\.connect\(\s*"` 全 `game/` 零命中 |
| core 层 await | 零命中 | 4 处匹配全在文件头注释里自述「零 await」 |
| core 超长函数 | 零命中 | 函数体 >120 行只在 presentation(`board_view._paint_cell` 142 行、`ui_kit.danger_button` 122 行) |
| 表 ↔ schema | 一一对应 | 19 `.tres` ↔ 19 顶层 schema,零孤儿表、零缺 schema;24 schema 全被消费 |
| resolver / cross_validator | 非测试专用 | 二者都在 `ConfigDb._ready → load_tables()` 产品路径真实运行 |
| 循环 preload | 零命中 | 全仓依赖图 DFS 仅 2 个自环,均在注释文本里 |
| 静态闸防空转自检 | 已内置 | S1–S13 各规则自带扫描面文件数为零即判红 |
---
## 三、P0 — 破坏既有纪律,已有实证
### 3.1 数值单一事实源被击穿(架构原则 2:代码零玩法数值字面量)
判据来自 `design/engineering/architecture/architecture.md` 架构原则 2 与 `code-conventions.md` 数值类硬纪律。下表分两类:**A 类=用户可观测的玩法数值写死在代码,直接违规**;**B 类=自标「机制结构常量/产出约束」,是否该入表属待裁,不计违规**。
#### A 类(18 处,直接违规)
| # | 位置 | 写死的值 | 表内真值 / 影响 |
|---|---|---|---|
| 1 | `game/presentation/main.gd:787` | 提示文案「弃图刷新需 8」 | `action_economy.tres` 的 `rap_refresh_cost`。紧邻 783 行是正确读表的,文案另写一份 |
| 2 | `game/presentation/main.gd:790` | 提示文案「每轮 2 次」 | 同表 `refresh_cap_per_round`。表改了玩家看到的数字不改 |
| 3 | `game/core/battle_orchestrator.gd:883` | GP 掉落乘数 `1.5` | 表内无此项;代码注释自认「乘数 provisional 150%」 |
| 4 | `game/core/node_map_generator.gd:380` | 岔口分支概率 `0.35` | `level_gen_params` 表内无此项 |
| 5 | `game/core/node_map_generator.gd:455` | rest/reward 排列起始骰 `0.5` | 表内无此项 |
| 6 | `game/core/node_map_generator.gd:361` | 岔口目标数上限 `24` | 表内无此项 |
| 7 | `game/core/effect_engine.gd:146` | 天赋「守」盾值 `1` | 表内无此项 |
| 8 | `game/core/effect_engine.gd:152` | 天赋「锋」伤害减半 `50` | 表内无此项 |
| 9 | `game/core/effect_engine.gd:151/154/161/164` | 天赋每关限次 `2` | 表内无此项 |
| 10 | `game/core/damage_controller.gd:435` | 直伤分量默认 `50`(百分比) | 表内无此项,`h.get("value", 50)` 兜底 |
| 11 | `game/core/battle_orchestrator.gd:507` | 变体成包大小下界 `2` | 只有上界读表 `variation.pack_max` |
| 12 | `game/core/settlement/combat_segment.gd:64` | 连击分段默认 `cap=2` / `divisor=3` | 表内无此项 |
| 13 | `game/presentation/playtest_logger.gd:16` | `ap_per_round := 8` | 与 `action_economy.tres` 同名值逐字重复 |
| 14 | `game/core/config_db_resolver.gd:118-119` | clamp 域 `0.1` / `10.0` | 与 `difficulty_table.gd` 的 `MULT_MIN`/`MULT_MAX` 重复但另写一份字面量 |
| 15 | `game/core/curio_deck.gd:98` | 令牌 cap 兜底 `99` | `config_db_cross_validator.gd:91` 注释写 cap ∈ [1,99],schema 无定义 |
| 16 | `game/core/shop_session.gd:155` | 货架位 `3` | 表内无此项 |
| 17 | `game/sim/level_autoplay.gd:29-34` | 失误三概率 `0.10` / `0.05` / `0.10` | 直接决定 MC 读数,属模拟口径的一部分 |
| 18 | `game/sim/level_autoplay.gd:410-428` | 选路评分 `30/5/10/80/20` | 同上 |
**最危险的是 #1 #2**:玩家会直接看到错误数字,且没有任何闸能发现「表值与文案数字不一致」。
**同族不一致(顺带查出)**:同调位上限在 `core/attunement.gd:14` 与 `core/meta_progression.gd:476` 写 `range(1, 4)`(上限 3),而 `presentation/views/hero_info_panel_view.gd:61/175` 写 `for i in 4`(4 槽)——两处口径已不一致。
#### B 类(自标结构常量,是否入表待裁,不计违规)
| 位置 | 常量 | 代码自述 |
|---|---|---|
| `game/core/node_map_generator.gd:12` | `MAX_WIDTH := 4` | 注释自标「产出约束·宽度=分叉生长」 |
| `game/core/action_economy.gd:14` | `CHARGE_LAYER_CAP := 3` | 注释自标「机制结构常量,非玩法表值」 |
| `game/core/board_data.gd:9` | `SPAWN_AREA_CELLS := 9` | 被 `data/schemas/board_gen_table.gd:77/106/107` 反向引用参与表校验——core 常量成了 data 侧校验的输入 |
| `game/core/node_map_generator.gd:83-94` | 层深下限 6、出口数域 [1,3]、岔口去路域 2/3 | 与 `level_gen_params.gd` 的域校验平行,两份域定义 |
---
### 3.2 sim 与 core 平行实现(四组)
`game/sim/` 15 件里 10 件是纯驱动器/采集器(调用 core,不重算规则),但有四组平行实现,其中两组直接导致「模拟读数 ≠ 游戏真实数值」。
| 组 | core 侧 | sim 侧 | 后果 |
|---|---|---|---|
| ① 扫雷推理引擎 | `board_generator.gd:175/204/217/234/249/265/286`(`measure_inferability` 族,输入含 `safe_set` oracle) | `auto_player.gd:114/154/181/188`(`_deduce` 族,输入 no-peek,且不做 0 格连锁算子) | 两边可推理率/卡死率不可直接换算。`auto_player.gd:80-83` 注释自认口径差异 |
| ② 伤害掷点期望 | `damage_math.gd` + `damage_controller.gd` 兑付管线 | `damage_probe.gd:196-201` 自建 `base_factor`/`rolled_base`/`floor` 取整顺序,只在末步复用 `pool_damage` | 探针的 ✓/✗ 比对基于自行重建的中间量 |
| ③ 档位排序与驱动循环 | `treasure_item_table.gd:44` 的 `TIER_RANK`;`sim/battle_driver.gd:17` 自称「驱动循环单点」 | `level_autoplay.gd:300-311` 另写一份档位排序;`level_autoplay.gd:521-575` 另写一份驱动循环(守卫阈值还不同:多出 `round_guard=60`、`rejects<3`) | 被声明为单点的东西存在第二份实现 |
| ④ 伪造表喂 core | 数值单一事实源 = `data/tables/*.tres` | `calibrate_boards.gd:14-19` 与 `experiment_density.gd:61-66` 用 `FakeDB` 伪造 `board_gen` 行喂 core 生成器 | 校准类读数绕开 SSOT(实验用途可理解,但无闸约束其扩散) |
---
## 四、P1 — 守卫覆盖缺口
### 4.1 RNG 单点纪律在 core 之外失守,且无闸
core 内干净,绕过点全在 core 之外:
| 层 | 位置 | 形态 | 是否被闸扫到 |
|---|---|---|---|
| presentation | `main.gd:914` | `randi()` 建种子,行内标 `rng-seed-exempt` | 扫到但被豁免标记排除 |
| probe | `ui_click_probe.gd:20` 等 8 处 | `RandomNumberGenerator.new()` | **不扫** |
| sim | `calibrate_boards.gd:54`、`experiment_density.gd:27`、`damage_probe.gd:274`、`level_autoplay.gd:388` | 自建 RNG + 显式定种 | **不扫** |
| tests | 3 处测试夹具 | 自建 RNG | **不扫** |
全部绕过点均显式定种、无 `randomize()`(全库零命中),确定性尚存;问题在于**没有任何闸防它扩散**——S1/S2 扫描面只有 `DIR_CORE` + `DIR_PRES`(`static_scan.sh:20-21, 88-91`)。
### 4.2 静态闸扫描面结构性缺口
`game/tests/static_scan.sh` 的 S1–S13 扫描面实况:
| 闸 | 扫描面 | 缺口 |
|---|---|---|
| S1/S2(RNG 单点) | core + presentation | sim / probe / tests 不扫 |
| S3(core 表加载单点) | core | — |
| S4(core 零引擎 UI 依赖) | core | — |
| S5(core 禁裸引 Autoload) | core | — |
| S6(core 信号禁内联 lambda) | core | — |
| S7(B1 编排唯一性) | core | 不管私有成员直取面(见 5.1) |
| S8(结算路径禁 await) | 9 文件显式清单 | 新增结算文件忘登记=静默不扫;缺位自检只防删改不防增 |
| S9(类型闸) | core + data + presentation | sim / probe / tests 不扫 |
| S13(gdstyle 质量闸) | core + data + presentation | 同上 |
| S11(术语禁用词)/S12(旧件残留) | game/ 全域 | 无缺口 |
净效果:**sim / probe / tests 三层除 S11/S12 外,零类型闸、零质量闸、零 RNG 闸、零 await 闸。**
### 4.3 S13 豁免常态化
三处豁免全落在体量最大、风险最高的文件上:
| 豁免对象 | 豁免内容 | 后果 |
|---|---|---|
| `core/battle_orchestrator.gd` | S13 全库单文件最大户,成员数阈值 15 豁免(实测键账 50→38 仍豁免) | 豁免后 S13 对它恒不约束,成员数再涨无感知 |
| `presentation/main.gd` | 46 个句柄成员 | 同上 |
| `presentation/tune_sim_controller.gd` | await 使用 | 同上 |
### 4.4 四个 core 文件零测试点名
判定口径:文件名与 `class_name` 在 `game/tests/` 全域 grep 均零命中。
| 文件 | 行数 | 职责 | 风险 |
|---|---|---|---|
| `core/attune_loadout.gd` | 117 | 同调配装件:装备中秘宝件对象集唯一所有者 | 有状态 |
| `core/battle_fx_host.gd` | 179 | 效果引擎宿主:fx 事件发射面、事实判定、AC/bonus 累积 | **有状态且在结算路径上** |
| `core/meta_state_store.gd` | 176 | 存档内元进度数据件(13 写口 + 6 只读投影) | **有状态且在存档路径上** |
| `core/slot_store.gd` | 189 | 存档槽位文件域件(挂载/激活/建删/摘要窥视) | 同上 |
`config_domain_combat` / `config_domain_run` / `config_domain_crossrun` 三个纯持有容器(零方法)零覆盖属可接受。
低覆盖(仅 1 个测试文件点名)另计:`hero_stats`、`skill_lottery`、`shop_session`、`curio_deck`。
---
## 五、P2 — 封装与漂移
### 5.1 settlement 五段类直读写编排器私有成员
`core/settlement/` 五段类作为「提取的方法对象」,直接读写主编排器的下划线私有成员 20+ 处:
- `combat_segment.gd:34/37` 写 `oc._damage.inject[...]`;`:52/53/58/77/78/80/89/91` 读 `oc._enemy_system.*`;`:123` 调 `oc._damage.flush()`
- `grant_segment.gd:96/121/151/178/229/289/333` 调 `oc._damage.open_ticket(...)`
- `aura_segment.gd:15/16` 调 `oc._emit_composition_effects(...)`;`decay_segment.gd:15` 调 `oc._status_effects.decay_round()`
GDScript 无编译期访问检查,私有成员改名不会有任何闸报错,只会静默断裂。S7 只管 B2/B3/B4 对外 API 符号收敛面,不管这个。
### 5.2 架构文档与代码漂移
| 文档位置 | 文档写的 | 盘上实况 |
|---|---|---|
| `architecture.md:110` | `core/hero_ledger.gd`「已实装」 | 该文件不存在;已拆为 `hero_stats.gd` / `gain_journal.gd` / `gp_wallet.gd` / `refresh_bank.gd` 四件。`tests/unit/core/hero_ledger_test.gd:171` 显式断言 `not FileAccess.file_exists("res://core/hero_ledger.gd")`,即代码层已把它当回归禁区 |
| `architecture.md:135` | `battle_orchestrator` 1313 行 / 50 成员 | 实际 1034 行(拆件后未回写) |
| `architecture.md` EC8 节 | 数据配置热重载能力(`set_reload_gate` 保护 `reload()`) | `ConfigDb.reload()` 全仓零调用方;且因走 `load()` 命中引擎资源缓存,即便调用也拿不到磁盘新值。门被 `battle_orchestrator.gd:295/861` 真实开合,但门后入口是死的 |
### 5.3 class_name 级双向依赖
`core/config_db.gd` → `data/schemas/board_gen_table.gd`(持有 `BoardGenTable` 实例)→ `core/board_data.gd:9`(取 `SPAWN_AREA_CELLS`)形成跨目录回边。双方均走 `class_name` 全局引用、未走 preload,不产生解析期死锁,但构成架构层面双向耦合。
同族:`core/node_map_generator.gd:10` 与 `data/schemas/level_table.gd` 各持一份 `NODE_TYPES` 词表(内容同、顺序不同)。
### 5.4 Autoload 形态不一致
四个 Autoload 里 `MetaProgression` 是唯一用 `get_node("/root/...")` 取依赖(`meta_progression.gd:26/28`)、唯一跨 Autoload 直调、唯一持 Node 引用的;另三个零交叉引用。注释说明是为绕 `--script` 模式 parse error,但与另外三个的构造注入形态不一致,且隐含 Autoload 加载顺序依赖(ConfigDB 须先 ready)。
---
## 六、待裁点
以下均需用户裁定,Agent 不代裁:
1. **A 类硬编码 18 处怎么修**——按改动四类归属,其中 `core/` 与 `presentation/` 的玩法数值入表属**需求改动**(写卡 + 编排),`sim/` 两处模拟口径常量属**工程改动**。两类不能混一批。
2. **B 类结构常量要不要入表**——`MAX_WIDTH` / `CHARGE_LAYER_CAP` / `SPAWN_AREA_CELLS` / `node_map_generator` 的域字面量四组,代码自标「非玩法表值」,但 `SPAWN_AREA_CELLS` 已被 data 侧校验反向引用,是否算玩法数值需裁。
3. **sim 平行实现的处置口径**——是收敛(sim 调 core 的推理与掷点)还是承认双口径并显式登记(避免读数混用)。四组里①②影响数值验证可信度,③④影响可维护性。
4. **闸面缺口补不补**——S1/S2/S9/S13 是否把 sim 纳入扫描面;S8 是否改成目录域而非 9 文件显式清单。
5. **S13 三处豁免的存废**——豁免已常态化到最大户文件上,是收回还是登记为长期例外。
6. **零测试点名的 4 个 core 文件**——补测试,还是登记豁免在册。
7. **文档漂移三处**——`architecture.md` 的 HeroLedger 登记行、battle_orchestrator 行数、EC8 热重载门表述,是否现在就按 doc-design 落回(纯文档面,主区直改)。
---
## 七、取证口径备注
- 全程只读,未跑测试、未跑引擎、未跑闸(测试两入口铁规)。
- 3.1 表 A 类 18 处中,#1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #13 #14 #15 #17 逐条 `sed` 取原文核过;其余按同一 grep 口径判定。
- 「零测试点名」判定词 = 文件 basename 与 `class_name` 全称,检索面 `game/tests/**/*.gd`;被当作类型间接引用与有针对性断言已分开计数。
- `static_scan.sh` 扫描面结论来自脚本自身变量定义(`DIR_CORE` / `DIR_PRES` / `SCAN_DIRS`)与 S8 的 `S8_FILES` 显式清单,非推测。
@@ -0,0 +1,126 @@
# 工程改动报告——存量断言精简 C 档(2026-10-28)
**批性质**:工程改动(纯测试维护),代码面独立 worktree 批(`assert-slim-b2-20261028`)。验证项目覆盖逐条不变,断言静态站点收敛。
**输入合同**:待办清单「存量断言精简——验证内容不变下的断言收敛」余账②(C 档手写族改表驱动循环)+用户 2026-10-28 会话裁决「六组全批(推荐)」。
**对账基准**:`game/tests/验证集清单.md` 冻结册(基点 `b9260138`,1587 条静态调用点;本批只比对不改写)。
## 1. 判据线
沿用 A+B 批判据线:**同一读数面 + 同一输入集 + 谓词字面等价**(C 档是同一族的逐例手写断言改为表驱动循环,每旧例成为循环表的一行数据,标签串保留在数据里可 grep)。
与 A+B 的区别:A+B 是删并(旧站点消失、判据由承接者承担);C 档是**形态改造**——旧例全部保留为循环的一行,执行条数不变,只有静态 `check(` 调用点下降。失败定位由「独立断言行」改为「违例明细串」(表内每行仍输出自己的标签,附带期望/实际)。
## 2. 删并申报表(33 旧站点 → 7 新站点)
「旧序号」=冻结册序号;「旧站点」=冻结册行号(`file:line` 省略相同路径前缀)。六组逐一列出,处置列写明合并形态。
### C1 关卡生成参数负向族(11 → 1)· `game/tests/unit/data_config/level_gen_params_test.gd`
| 旧序号 | 旧站点 | 旧标签 | 新站点 | 处置 |
|---|---|---|---|---|
| #1221 | `_test_negative:72` | 合法三行域校验零错误(夹具自证)〔fv2〕 | `:107` | 表行 `cases[0]`(夹具自证行保留在表首) |
| #1222 | `_test_negative:75` | L1 出口=2 被拒(出口 L1 恒 1 已锁)〔fv2〕 | `:107` | 表行 `cases[1]` |
| #1223 | `_test_negative:78` | 配比合计 0.5 被拒(四类合计 1 已锁)〔fv2〕 | `:107` | 表行 `cases[2]` |
| #1224 | `_test_negative:81` | 层深 5<6 被拒(层深下限已锁)〔fv2〕 | `:107` | 表行 `cases[3]` |
| #1225 | `_test_negative:85` | 层深增幅倒挂被拒(递增且增幅变大)〔fv2〕 | `:107` | 表行 `cases[4]` |
| #1226 | `_test_negative:88` | 出口 4 越域 [1,3] 被拒〔fv2〕 | `:107` | 表行 `cases[5]` |
| #1227 | `_test_negative:92` | 岔口去路数 (2,4) 被拒(恒 2/3 已锁)〔fv2〕 | `:107` | 表行 `cases[6]` |
| #1228 | `_test_negative:97` | 字段键集非 ROW_KEYS 闭集被拒〔fv2〕 | `:107` | 表行 `cases[7]` |
| #1229 | `_test_negative:100` | 键重复行被拒〔fv2〕 | `:107` | 表行 `cases[8]` |
| #1230 | `_test_negative:102` | 战斗保底 -1 被拒(≥0)〔fv2〕 | `:107` | 表行 `cases[9]` |
| #1231 | `_test_negative:105` | 空表域校验零错误(合法态)〔fv2〕 | `:107` | 表行 `cases[10]` |
新站点标签模板 `域校验:%s(期望%s,实际%s)〔fv2〕`:`%s` 即旧标签原文(表内逐字保留),`期望/实际` 附违例明细串。
### C2 RNG 黄金向量族(8 → 1)· `game/tests/unit/data_config/rng_stream_boot_test.gd`
| 旧序号 | 旧站点 | 旧标签 | 新站点 | 处置 |
|---|---|---|---|---|
| #1239 | `_smoke_initialize:37` | 统一流向量 m=42 scope=0(=旧 board_gen scope0 连续性锚) | `:40` | `golden[0]`(循环标签补注 scope0 锚语义) |
| #1240 | `:38` | 统一流向量 m=42 scope=1 | `:40` | `golden[1]` |
| #1241 | `:39` | 统一流向量 m=42 scope=2 | `:40` | `golden[2]` |
| #1242 | `:40` | 统一流向量 m=42 scope=3 | `:40` | `golden[3]` |
| #1243 | `:41` | 统一流向量 m=42 scope=4 | `:40` | `golden[4]` |
| #1244 | `:42` | 统一流向量 m=42 scope=5 | `:40` | `golden[5]` |
| #1245 | `:43` | 统一流向量 m=42 scope=6 | `:40` | `golden[6]` |
| #1246 | `:44` | 统一流向量 m=42 scope=7 | `:40` | `golden[7]` |
黄金向量八值逐字不变;标签由逐行写死改为 `scope=%d` 循环变量,grep 锚 `统一流向量 m=42 scope=` 保留。
### C3a 奖励点头部禁词族(3 → 1)· `game/tests/unit/presentation/player_copy_sweep_test.gd`
| 旧序号 | 旧站点 | 旧标签 | 新站点 | 处置 |
|---|---|---|---|---|
| #1383 | `_sweep_narrator:59` | 奖励点头部零「同权重」旧话(u3 清零)〔wa4〕 | `:60` | 禁词表行「同权重」 |
| #1384 | `:60` | 奖励点头部零「同上限」旧话(u3 清零)〔wa4〕 | `:60` | 禁词表行「同上限」 |
| #1385 | `:61` | 奖励点头部零「提升池」旧话(u3 清零)〔wa4〕 | `:60` | 禁词表行「提升池」 |
标签模板 `奖励点头部零「%s」旧话(u3 清零)〔wa4〕`,禁词作参数,与旧标签逐字同形。
### C3b 图例封印行禁词族(2 → 1)· 同文件
| 旧序号 | 旧站点 | 旧标签 | 新站点 | 处置 |
|---|---|---|---|---|
| #1389 | `_sweep_legend:76` | 图例封印行零「插错」(u4 旗族遗留词干清零)〔wa5〕 | `:76` | 禁词表行「插错」 |
| #1390 | `:77` | 图例封印行零「挖雷」(u4 雷字清零)〔wa5〕 | `:76` | 禁词表行「挖雷」 |
标签模板 `图例封印行零「%s」〔wa5〕`;`seal_row` 取值表达式(`size > 2` 守卫)逐字保留在循环体内。
### C4 站桩蓄力置位四入参(4 → 1)· `game/tests/unit/presentation/standee_fx_sweep_test.gd`
| 旧序号 | 旧站点 | 旧标签 | 新站点 | 处置 |
|---|---|---|---|---|
| #1407 | `_init:95` | 蓄力技能(heavy)置 `_intent_charge=true`〔wd2〕 | `:103` | 入参表行 `["damage","heavy",true,…]` |
| #1408 | `:97` | 非蓄力攻击技(goblin_slash)零置位〔wd2〕 | `:103` | 入参表行 `["damage","goblin_slash",false,…]` |
| #1409 | `:99` | 增益系零置位〔wd2〕 | `:103` | 入参表行 `["buff","goblin_rage",false,…]` |
| #1410 | `:101` | 普攻零表示路径复位 `_intent_charge`〔wd2〕 | `:103` | 入参表行 `["damage","basic",false,…]` |
判定式 `s1._intent_charge == 期望` 与四旧例同谓词;`set_pending` 入参与旧例逐项相同。
### C5 字体字形集三组(3 → 1)· `game/tests/unit/presentation/ui_font_asset_test.gd`
| 旧序号 | 旧站点 | 旧标签 | 新站点 | 处置 |
|---|---|---|---|---|
| #1583 | `_init:18` | 中文字形在集(攻/雷/旗/封) | `:30` | 字形组「攻雷旗封」 |
| #1584 | `:20` | 中文字形在集(危——已开危险格字形换代前置,迭代36 u12/wa9) | `:30` | 字形组「危」 |
| #1585 | `:21` | ASCII/数字字形在集(A/0/%) | `:30` | 字形组「A0%」 |
判定由逐字 `has_char` 合取改为组内逐字扫描,标签附缺字明细(缺字集为空即绿)。
### C6 锻造工作间长剑/轻甲双行(2 → 1)· `game/tests/unit/presentation/forge_workbench_test.gd`
| 旧序号 | 旧站点 | 旧标签 | 新站点 | 处置 |
|---|---|---|---|---|
| #1336 | `_run:63` | 长剑行:购买钮「锻造 20」(0 级位几何价)+选中钮「当前」禁用〔锻造工坊批 V13〕 | `:64` | 行表 `["sword","长剑"]` |
| #1337 | `:66` | 轻甲行:购买钮「锻造 20」+选中钮「当前」禁用〔锻造工坊批 V13〕 | `:64` | 行表 `["light","轻甲"]` |
**谓词取并集申报**:旧 #1336 含 `not buy_btn.disabled` 与 `select_btn != null` 两子句、#1337 缺这两子句。合并后统一用 #1336 全谓词(**只增不减**,轻甲行增补 `select_btn != null` 与 `not buy_btn.disabled` 两子句)——已在用户裁决时呈报并随「六组全批」批准。标签统一为 `%s行:…(0 级位几何价)…`,两种行名均保留可 grep。
## 3. 件核
| 项 | 改前 | 改后 | 判据 |
|---|---|---|---|
| 六套件静态站点 | 33 | 7 | 冻结册逐行比对 |
| 全清单静态条数(`--diff`) | 1587 | **1561** | `python tools/extract_checks.py --diff game/tests/验证集清单.md`:消失 33/新增 7,与上表逐条对应 |
| 运行期条数(六套件) | 17/13/22/18/5/12 | 17/13/22/18/5/12 | 合并前后同值;`ui_font 5<6`、`forge 12<13` 的静态差为失败分支站点(装载失败/字体缺失 `Kit.check(…, false)`),绿跑不执行,改前即如此 |
| 套件全量回归 | — | 65 套件全绿 | `unit.log`:`[run_all] all green (65 suites)` |
| 全码域闸 | — | all_green | 回执 commit `fd6cf968`:domain=code、dirty_game=false;01/02/10 绿;09 存量红指纹 `8467b4588a20` 零新增;00/03/04/05/11 影响面选闸 SKIP(diff 全在 `game/tests/` 测量面) |
| 合并 | — | `87d241c1` | 合并后主区 `--editor --quit` 退出 0;`python tools/run_smoke.py` PASS(15 项检查,三硬闸全过) |
运行期条数不变+每个旧标签都在循环数据里逐字保留=**验证项目覆盖逐条不变**的直接证据;静态条数降 26=本批唯一预期变化。
## 4. 余账与去向
| 余账 | 处置 |
|---|---|
| 类别②(内部面断言被入口严格承载) | A+B 批实测 0 条,不再找 |
| C 档候选 | 六组全数执行,C 档本波清零;无剩余候选 |
| D 档四处(theme:215/217 × type_glow:148/150、level_shop_economy 现行图 vs 扩面段) | 2026-10-28 用户裁全保留,不属精简账 |
| 恒真与名实不符断言五条 | 判据缺陷另账 → 待办清单新登记「恒真与名实不符断言五条——判据缺陷修正」(`feat_vouchers:51`/`treasure_effects:263`/`level_shop_economy:593`/`:696`/`system_contracts:66-67`) |
| `Kit.check_quiet` 静态站点 407 个在提取器口径外 | → 待办清单新登记「断言提取器改版——覆盖 Kit.check_quiet 静默通道」 |
## 5. 纪律自检
- 无删断言、无放宽阈值、无缩小扫描面、无下层直调替换:33 条判据全部保留为循环表行,输入集与判定式逐条对应(C6 为谓词并集,已申报)。
- 无变异法、无撤销类取证;绿由保持断言复跑取得(整套件全绿)。
- 冻结册未动一字,`--diff` 差异面与申报表逐条对齐。
@@ -0,0 +1,46 @@
# 盘点:验证与清单体系(阶段 0)
> 2026-10-28 · 服务待办清单「验证与清单体系整改——四方向分阶段推进」条
> 性质:过程件(吸收即死)——结论吸收进待办条目与阶段 1 设计文档后删除。
## 1. 结论
1. **验证集清单零机器闸依赖**:`tools/run_all_gates.sh`、hooks、五个项目技能均不引用它;规范性锚点只有 `design/engineering/test-conventions.md` §8.3 一处。退役不需要改闸、改钩子、改技能。
2. **测试名尾缀不是全量追溯键**:47 个有断言的文件中 19 个零尾缀(含三条 35 条以上断言的大套件)。能力清单只能按「能力→代表用例」建,不能要求逐断言尾缀。
3. **对照表与待办清单都有机器闸**:前者的列结构、后者的条目格式均由 `tools/pipeline_gate.py` 强制;这两处瘦身要动闸,属阶段 3/4,不是阶段 1 的顺手活。
4. **能力清单已有现成形态**:`game/tests/README.md` 的「集成旅程清单」5 行,扩面成本低;`repo_structure_audit.py` 不限制 `game/tests/` 下的 .md 文件名。
## 2. 依赖面盘点
| 件 | 消费方 | 机器闸依赖 | 退役/改动成本 |
|---|---|---|---|
| `game/tests/验证集清单.md` | test-conventions §8.3、待办清单 1 条、2 份过程报告、`tools/extract_checks.py` | 无 | 低:改 1 条规约+删文件 |
| `tools/extract_checks.py` | 无自动调用;手工生成/对账 | 无 | 可保留为审计工具,或随清单退役 |
| 改动—验收对照表 | req-card 技能、iter 技能、`pipeline_gate.check_ledger`(强制 5 列) | 有 | 中:规约+两技能+闸三处 |
| 待办清单 | `pipeline_gate`(格式全套)、`doc_ref_audit`、`doc_term_audit`、`repo_structure_audit`、各技能 | 有 | 低:正文压缩不触机检;新增「≤5 行」规则才动闸 |
| 独立验收审计 | iter 技能、req-card 技能、`pipeline_gate`(只查「验收审计」字样) | 有(字样) | 中:改抽样规则要动两技能+规约 |
| 集成旅程清单 | 无机器消费 | 无 | 低:直接扩面 |
## 3. 覆盖数据
- `game/tests/**/*.gd` 共 70 个文件;静态 `check(` 粗口径 1607 处(含 `test_kit` 定义行与 `smoke_boot` 等提取器口径外宿主),清单口径 1587 处。
- 标签尾缀〔…〕756 个(上限口径,一条标签一个)。
- 有断言的文件 47 个:28 个有尾缀,19 个零尾缀。
- 零尾缀大套件:`seal_polarity` 69、`variant_placement` 42、`roll_multiplier` 35、`cross_system_flow` 22、`damage_math` 21、`skill_unlock` 20、`type_glow` 20、`minesweeper_rules` 16、`system_contracts` 15。
- 静默收集通道 `check_quiet` 约 407 站点在提取口径外(既有备案,test-conventions §5 工具局限)。
## 4. 对四阶段的影响
| 阶段 | 盘点结论对它的约束 |
|---|---|
| 1 换尺子(A) | 先扩「集成旅程清单」到核心域;每行=能力|真实入口|代表用例|最后验证批次|缺口。退役验证集清单;`extract_checks.py` 去留在设计讨论裁 |
| 2 按新尺子精简(B) | 删并对象改为能力项;每项能力保留至少一个代表用例;不要求逐断言尾缀 |
| 3 调验收强度(C) | 对照表列与审计抽样一起改,触 `pipeline_gate`、req-card、iter 三处;不要与阶段 1 同批 |
| 4 文档减肥(D) | 待办正文压缩可直接做;「≤5 行」入闸需改 `pipeline_gate` |
## 5. 未决点(阶段 1 设计讨论)
1. 能力清单落点:`game/tests/README.md` 扩面,还是独立文件?
2. 能力项粒度与覆盖面:按哪些域切、预计多少行?
3. `tools/extract_checks.py` 去留:随清单退役,还是保留为审计工具?
4. 验证集清单退役时点:立即冻结,还是阶段 1 设计定稿后一并退役?
@@ -0,0 +1,193 @@
# 评估:Dungeon Sweeper 美术资产清单与性能预算
> 版本 v1 | 2026-10-30 | 目标引擎 Godot 4.7.2-stable(`gl_compatibility` / 纯 2D)| 平台 PC Windows 先行 | 设计分辨率 1280×800 基座 + 1920×1080(全局 1.35× 等比)
> 性质:评估过程件,对应美术师交付物「美术资产清单」+「美术规格与预算」两章合并(一次问三事,不拆两份重复)。**未改任何权威文档与 `game/` 文件**;结论若要进 `design/` 走 doc-design 登记 + 用户发令。
> 事实源:`design/art/art-assets-checklist.md`(需求)· `art-status-checklist.md`(现状)· `art-bible.md` §8(尺寸定值单点)· `asset-path-map.md`(路径)· `game/assets/` 实扫 · `game/project.godot` 与 117 份 `.import`(引擎现状)· `design/gdd/ux/battle.md` 板形表 · `game/data/tables/enemy.tres`。
> 显存口径:`art-engine-handoff/scripts/asset_budget.py` 实算;格式按项目真实导入(`compress/mode=0` Lossless → 运行时解码为 RGBA8 = 4 B/px,关 mipmap)。Draw Call 与峰值显存**未真机实测**,只给内控线与估算口径。
## 0. 结论先行
1. **纹理显存不是瓶颈**:现状 126 张 PNG 解码后合计 **0.82 MB**,占项目内控线 96 MB 的 0.85%。
2. **满配试算 39.95 MB**,占内控线 41.6%,余量 56.05 MB(≥20% 达标);前三项占 96.9%,压显存只需盯营地场景图。(营地由 9 张拆为 10 张:新增 `scene_camp_wall` 围墙层,按细条 `(0,240,1280,70)`=0.34 MB 计,不做全幅;较原口径净增 0.34 MB。)
3. **真瓶颈在 Draw Call 与包体结构**:196 格棋盘 + 碎图各自独立纹理,最重一屏粗估 400–600 次;磁盘 6.87 MB 中字体独占 6.68 MB(97%)。
4. **导入设置现状正确,无需改**:117 张全 Lossless + mipmap 全关 + 全局 Nearest,与像素风四纪律一致;非 2 幂尺寸在当前 PC Lossless 下不产生补齐,脚本 warn 可忽略。
5. **1920×1080 档不新增纹理**:全局等比缩放复用同一份资源,显存与档位无关。
## 1. 目标引擎与导入基线
### 1.1 工程侧已核事实(实测,非文档推断)
| 项 | 值 | 来源 |
|---|---|---|
| 引擎 | 4.7.2-stable,`config/features=PackedStringArray("4.7")` | `project.godot` |
| 渲染方法 | `gl_compatibility`(PC OpenGL Compatibility) | `project.godot` |
| Stretch | `canvas_items`,1280×800 视口;1920 档 aspect keep | §8.1/§8.2 |
| 默认过滤 | `default_texture_filter=0`(Nearest) | §8.2,全局像素风基线 |
| 导入类型 | `CompressedTexture2D`,`vram_texture: false`,117/117 | `.import` 全量 |
| 压缩 | `compress/mode=0`(Lossless),117/117 | `.import` 全量 |
| Mipmap | `mipmaps/generate=false`,117/117 | `.import` 全量 |
| 3D 检测 | `detect_3d/compress_to=1`(2D 项目不触发) | `.import` 全量 |
| 透明边 | `process/fix_alpha_border=true`,117/117 | `.import` 全量 |
| 字体 | `theme/custom_font` = Fusion Pixel 单套 TTF | §3.6 清单已定稿 |
**Lossless 的显存真相**:`compress/mode=0` 只压缩磁盘上的 `.ctex`,运行时仍解码成 RGBA8 上传显存(`vram_texture: false` 佐证),故显存一律按 **4 B/px** 计,不按 PNG 文件大小计。这是本次全部试算的口径。
**非 2 幂 warn 的边界**:实扫 126 张中大量 20×20 / 46×48 / 66×94 等非 2 幂尺寸。在当前 **PC + Lossless** 下不补齐、不影响压缩,脚本提示可忽略;若日后转 VRAM 压缩或上移动端/小游戏,该项即变为硬约束(无法压缩或图集补齐),属「待实测项」。
### 1.2 Godot 4.7 导入设置对照表
按 `engine-import.md` 逐项填,只列目标引擎列与项目现状;3D 相关项因纯 2D 标不适用。
| 项 | 通用要求 | Godot 4.7 项目现状 | 判定 |
|---|---|---|---|
| 纹理类型 | 2D 项目用 Sprite2D / TextureRect | `CompressedTexture2D` | ✅ |
| 压缩 | 像素风 = 关闭压缩或无损 | Lossless(117/117) | ✅ 正确;插画级大图是否转 VRAM 压缩待裁 |
| sRGB | 颜色图勾 sRGB | 走 PNG 色彩空间默认,`hdr_as_srgb=false` | ⚠ 建议显式逐张标注色彩空间(待核,无风险) |
| Mipmap | UI / 精灵 / 特效关闭 | 全关 | ✅ |
| 过滤 | Point(像素风) | 全局 Nearest;显式 LINEAR 豁免仅 vignette / gradient_base / beams 三件,已入册 | ✅ |
| 九宫格 | StyleBoxTexture 设边距 | `scene_stage_plaza` 48×12 横向拉伸、顶缘 2px 不随拉伸变形;UI 面板走主题样式盒 | ✅ 边距需随件填,不靠记忆 |
| 图集 | 同屏资产归同组 | **现无图集**,126 张独立纹理 | ⚠ Draw Call 处置方向,待工程评估 |
| 字体 | 动态字体优先,授权干净 | Fusion Pixel 单套,SIL OFL 1.1 在册 | ✅ |
| 序列帧 | 播放器关插值、行列写进规格 | 帧级素材未产;帧数已冻结(待机 4 / 攻击 6 / 受击 2,§8.4) | ⏸ 待产 |
| 3D 模型 / 法线 / PBR / LOD | 单位轴向、OpenGL 法线 | **不适用**(纯 2D,`mdl` / `tex` 类为空) | — |
## 2. 资产清单(运行时资源,十类逐一过)
### 2.1 十类现状总表
优先级沿用 `art-assets-checklist.md` 的 P0–P3 口径(玩法依赖,非排期);状态取自 `art-status-checklist.md`。前缀为美术师通用分类,项目文件命名以 §8.3 为准(见 2.3)。
| 类 | 前缀 | 项目对应资产 | 在盘件数 | 状态 | 优先级 |
|---|---|---|---|---|---|
| 角色 | `char` | 主角 B 区三态立绘 + 棋盘小人 32×32 四向 | 13(`art/character`) | ✅ 三态已接 | P1 |
| 怪物 | `mon` | 八敌站桩立绘(36–94 逐键定档) | 13(同上,含敌) | ✅ 已服役 | P1 |
| 场景 | `env` | 砖面/地形 `terrain_*` 三关换皮、封印印记、舞台地台 | 64(`art/board`)+1(`art/scene`) | ◐ 砖面程序化观感待升级 | P2 |
| UI | `ui` | 面板/按钮/进度条/节点图容器 | 33(`art/ui`) | ◐ 面板与节点图占位 | P2 |
| 图标 | `icon` | 状态九枚、格面徽章 19、护甲三枚、柜台四枚、节点四类 | 12(`assets/ui/icons`)+19 待产 | ◐ 部分在役 | P2 |
| 特效 | `fx` | 挥铲三帧、打击火花、烟尘碎屑、封印特效 | 3(`art/fx`) | ◐ 程序化在役 | P3 |
| 动画 | `anim` | 意图准备三档、死亡灰化碎裂、弹道密度 | 0(帧级未产) | ◐ 占位符号在役 | P1/P3 |
| 3D 资产 | `mdl` | — | 0 | **不适用** | — |
| 字体 | `font` | Fusion Pixel 单套单字重 | 1 TTF(6.68 MB) | ✅ 已定稿 | P2 |
| 插画 | `img` | 营地场景图 10 张(9 全幅+1 围墙细条)、B 区舞台背景 1280×270 | 0(全占位) | ⬜ 未开始 | P2/P3 |
在盘合计:PNG 126 张(`art/` 114 + `ui/icons/` 12)+ TTF 1 件;磁盘 6.87 MB。**纯 2D,零 3D 资产,无法线/PBR/LOD 需求。**
### 2.2 缺口清单(未 ✅ 项,P0 写清阻塞什么)
| # | 资产 | 类 | 规格(§8.2 定值) | 用途 | 优先级 | 状态 | 阻塞什么 |
|---|---|---|---|---|---|---|---|
| 1 | 战斗一屏样张 | 插画/样张 | 1280×800 双舞台整屏 | 锁剪影/动画/战斗空间/敌人层 | **P0** | ⬜ | 角色终稿、意图动画、演出类全部不得开工(清单 §1.2 硬前置) |
| 2 | 意图准备动画三档 | 动画 | 待机 4 / 攻击 6 / 受击 2 帧,灰度剪影可辨 | 敌方下一拍预告 | P1 | ◐ | 预告从占位符号退场;文字/技能名退出徽标后的唯一承载 |
| 3 | 格面资源徽章 19 枚 | 图标 | 32×32 级,极性=金满框/红缺框 | 九家族正负 + 清图槽 | P2 | ◐ | 无(占位够试玩,观感不达标) |
| 4 | 雷/封印/数字砖面全套 | 场景 | 32×32 瓦片随格边长整格铺满 | 扫雷核心读盘 | P2 | ◐ | 前置格子样张已 ✅,无硬阻塞;观感升级 |
| 5 | 关卡节点图图标组 | 图标 | 四类节点 + 行进器 | 节点图 UI | P2 | ◐ | 正式节点图 UI 设计未成文 |
| 6 | 营地场景图 10 张(9 全幅+1 围墙细条) | 插画 | 2× 源出图 → 降采样 1280×800 | 营地交互重做 | P2 | ◐ | 营地交互重做美术自立批(不占迭代位) |
| 7 | 护甲类型图标三枚 | 图标 | 24×24,显示 1× | 站桩名字行 | P2 | ⬜ | 名字行 ◇/◆ 字形占位退役 |
| 8 | B 区舞台背景 | 插画 | 1280×270,远景+中景两带 | 战斗舞台 | P3 | ⬜ | 临时光柱氛围层由背景吸收 |
| 9 | 受击/死亡/顺位、连锁坍塌、封印特效、弹道密度 | 特效/动画 | 灰化+碎裂退场(禁瞬消) | 战斗打磨 | P3 | ◐/⬜ | 无硬阻塞,样张后排期 |
| 10 | 状态九枚 / 字体 / 主角三态 / 八敌立绘 / 地台 | — | — | — | — | ✅ | 已交付,不列为缺口 |
格子样张 ✅ v3 冻结(2026-09-30 机检 17/17);收益/秘宝图标、封面宣传图、地形变异专属纹理按清单 §5 **明确不在本清单**,不计入预算。
### 2.3 命名口径冲突(呈报)
美术师通用规范为四段式 `<类别>_<对象>_<状态>_v<N>.png`(旧版改名 `_deprecated` 不删);项目 §8.3 已定案为**域键式** `<域>_<对象键>_<状态|变体>[_<序号>].png`,对象键 = 数据表主键(`effect_id`/`reward_id`/`cell_type`/`enemy_id`),文件名键全局恒定禁运行时换图,且素材不入 git、候选与终稿分目录、版本号走归档纪律。
**处置:以项目 §8.3 为准**,通用四段式不覆盖。依据:项目规范已定稿且有代码闸(`standee_asset_check`、图标键契约)依赖该命名;本条仅呈报差异,不擅自改。
### 2.4 产出批次
沿用清单 §6 依赖链,不重排:格子样张(✅)→ 砖面全套;战斗一屏样张(⬜)→ 角色终稿(✅)→ 意图动画 → 演出类;徽章/节点图/效果图标/复用件为独立线可插队;营地场景图归营地美术自立批。**第 1 批基准图未定稿前不排后续批次规格。**
## 3. 性能预算(六项,先预算后规格)
### 3.1 倒推起点:同屏对象数(已核)
| 对象 | 上限 | 来源 |
|---|---|---|
| 棋盘格 | 14×14 = **196 格**(格边长 30px) | `battle.md` 板形表 N=14 |
| B 区单位 | 主角 1 + 敌最多 **3** | `enemy.tres` `unit_count_max` 全表最大 = 3 |
| HUD / C 区 | 数据框、状态槽、按钮组约 60 件 | 现役 UI 结构 |
| 营地 | 背景 1 + 可点物件 7 + 承载件 1 = 9 | 清单 §3.5 |
### 3.2 六项预算表
内控线 = 本项目自定(通用 PC 档为起步值,需实测校准);均留 ≥20% 余量;实测日期栏为「未实测」的项不得当验收结论。
| 维度 | PC 档参考 | 项目内控线 | 现状/试算 | 判定 | 备注 |
|---|---|---|---|---|---|
| 纹理内存 | ≤ 1.5 GB | **96 MB** | 现状 0.82 / 满配 39.95 MB | ✅ | 余量 56.05 MB;可用线(20% 余量)76.8 MB |
| Draw Call | ≤ 800 | **600** | 估 400–600,**未实测** | ⚠ | 留 25% 余量;真机跑最重一屏取峰值 |
| 同屏三角面 | ≤ 100 万 | 不构成约束 | 约 0.14 万(2D quad 2 三角) | ✅ | 远低于档位,仅记录 |
| 同屏粒子 | ≤ 1000 | **450** | 程序化火花/碎屑,未实测 | ⚠ | 建议单次特效峰值 ≤150、同屏 ≤3 |
| 单角色骨骼 | ≤ 150 | **不适用** | 0(无骨骼,序列帧+程序化) | — | 纯 2D |
| 包体(磁盘) | PC 首包不适用 | 记录值 | **6.87 MB**(PNG 0.19 + 字体 6.68) | ✅ | 字体占 97%,压包体先做字体子集化而非压图 |
**内控线 96 MB 的依据**:通用 PC 档 1.5 GB 面向 3D/4K,本项目 2D 单场景全驻留最坏试算 39.95 MB;取 ~2.4× 作内控线,给三关场景、秘宝图标与后续插画留扩展空间,并满足 ≥20% 余量。
### 3.3 纹理内存试算(脚本实算,非估)
| 版本 | 项数 | 合计 | 占内控线 | 余量 | 前三名占比 |
|---|---|---|---|---|---|
| 现状版 | 20 种尺寸 / 126 张 | **0.82 MB** | 0.85% | 95.18 MB | 75.3%(32×32×78 = 0.30 MB、256×256×1 = 0.25 MB、64×64×4 = 0.06 MB) |
| 满配版 | 26 项 / 189 件 | **39.95 MB** | 41.6% | 56.05 MB | 96.9% |
满配增量清单口径(全部 RGBA32、mipmap 关):
| 增量项 | 规格 | 件数 | MB | 占满配 |
|---|---|---|---|---|
| 营地场景图 | 1280×800 ×9 + 围墙细条 1280×70 ×1 | 10 | **35.50** | 88.9% |
| 敌人动画图集 | sheet:64×64 帧、12 帧、6 列、pad 2 → 408×136 | 9 套 | 1.91 | 4.8% |
| B 区舞台背景 | 1280×270 | 1 | 1.32 | 3.3% |
| 徽章/节点/护甲等图标 | 32×32 | 36 | 0.14 | 0.4% |
| 特效帧/粒子贴图 | 64×64 | 6 | 0.09 | 0.2% |
| 主角动画图集 | sheet:48×72 帧、12 帧、6 列、pad 2 → 312×152 | 1 套 | 0.18 | 0.5% |
| 现状存量 | 实扫聚合 | 126 | 0.82 | 2.1% |
按格式聚合:RGBA32 100%(项目全 Lossless)。**现状与满配差 39.13 MB,其中营地 10 张独占 90.7% 的增量。**(围墙层落盘按细条 `(0,240,1280,70)`=0.34 MB,不占 1280×800 全幅大图名额。)
### 3.4 由预算倒推单件规格(≥20% 余量)
1. **大图是唯一约束面**:可用线 76.8 MB 减去非大图 4.45 MB = 72.35 MB;1280×800 @ RGBA32 单价 3.906 MB → **同档大图总量 ≤ 18 张**(18 张 = 70.31 MB,合计 74.76 MB ✅;19 张即 78.67 MB ❌)。营地首批 9 张全幅大图(另加 1 张围墙细条 0.34 MB,不占大图名额)安全,但「三关各 9 张全幅大图 = 27 张」必然触发超线。
2. **触发后的处置优先级**:① 大图换 VRAM 压缩(BC3 = 1 B/px,单价降至 0.977 MB,27 张仅 26.4 MB);② 或降入包分辨率至 960×600。**像素小图不动**——BC 块状伪影会毁像素边缘,且它们只占 0.82 MB。
3. **动画图集省的是 Draw Call,不是显存**:9 套图集 1.91 MB,同帧数逐帧独立 PNG 反而只需 1.69 MB(图集 padding 与补齐多出 0.2 MB)。进图集的收益在合批,别拿它当省显存的理由。
4. **小图/图标无需限规格**:32×32 级 78 张仅 0.30 MB,继续按 §8.2 甲类整数倍纪律产出即可。
5. **1920×1080 档零增量**:全局 1.35× 等比缩放复用同一份资源,不产生第二套纹理,显存与档位无关(§8.2 已裁)。
### 3.5 Draw Call 控制(美术能做的部分)
现状 126 张独立纹理、零图集,是 Draw Call 的结构性来源。最重一屏估算口径(**非实测**):196 格 ×(瓦片纹理 + 程序化覆盖层)≈ 390,B 区 4 单位 × 4 件 ≈ 16,HUD/C 区 ≈ 60 → **400–600 区间**,逼近内控线 600。
| 风险 | 处置方向 | 归属 |
|---|---|---|
| 三关瓦片 64 张交替绘制打断合批 | 同关同族瓦片合并为 `AtlasTexture` | 工程面改动,待裁/工程评估 |
| UI 切图过碎、每图标一材质 | 战斗页 / 营地页 / 节点图分页分图集 | 同上 |
| 特效贴图各自独立 | 特效帧进同一图集,粒子用同材质染色 | 同上 |
| 程序化 `_draw` 覆盖层 | 与纹理绘制混排会断批,排绘制顺序时可收敛 | 同上 |
| 动态字体 | Fusion Pixel 单套已合批,保持 | ✅ 无需动 |
**本报告只列处置方向,不新增需求、不改工程面**;任何一条落地须由用户发令走对应通道。
### 3.6 待实测项(未跑,发起权归用户)
| 项 | 方法 | 输出 | 状态 |
|---|---|---|---|
| 最重一屏峰值 Draw Call | 真机跑 14×14 满敌战斗场景 | 峰值次数 vs 内控 600 | **未实测** |
| 峰值显存 | 真机含营地切场景往返 | vs 96 MB 内控线 | **未实测** |
| 粒子峰值 | 同屏 3 特效叠加 | vs 450 内控线 | **未实测** |
| VRAM 压缩可用性 | GL Compatibility 下试导 BC3 大图 | 画质目检 + 显存实测 | **未实测** |
| 1920 档观感 | 1.35× 非整数缩放目检 | 甲类件宽窄不均是否可感 | 未目检 |
**禁止**用编辑器或模拟器数据当验收结论(实测差 2–3 倍);上表机型与实测日期留空待填。
## 4. 假设与待确认
1. 假设:目标平台 = PC Windows 先行(AGENTS.md),暂不上移动端/小游戏 → 无首包硬限、压缩不走 ASTC/ETC2。
2. 假设:满配 = 营地 10 张(9 全幅+1 围墙细条) + 舞台背景 1 + 9 敌动画 12 帧/套 + 主角 1 套 + 图标 36 + 特效 6;秘宝图标/封面宣传图按清单 §5 排除。
3. 假设:同屏敌人上限 3(`enemy.tres` `unit_count_max` 全表最大=3,已核)、棋盘 N=14(`battle.md` 板形表,已核)。
4. 假设:动画帧尺寸按 64×64 源档均值估(实际立绘逐键 36–94),帧数按 §8.4(4+6+2=12/套);量级不受影响。
5. 待确认:营地 10 张是否同屏常驻(Godot 引用计数不自动卸载,本报告按最坏全驻留计);动画是否走 `SpriteFrames` 图集(影响 Draw Call 估算,不影响显存量级)。
## 5. 不改动声明
未改 `design/art/` 任何权威清单与圣经、未改 `game/` 任何文件;未生成图、未跑测试、未跑 MC(无数值改动)。本报告为过程件,按 `reports/README.md`「吸收即死」:结论若被权威文档吸收即可删,若需落设计文档则走 doc-design 登记 + 用户发令。