Files
gplan-2/design/dev/architecture/adr-0003-scene-structure-and-board-rendering.md
T

140 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0003: 场景结构与棋盘渲染(Scene Structure & Board Rendering)
## Status
Accepted(2026-09-12,solo 模式用户授权代决;迭代期可推翻重议)
**部分修订(2026-09-23)**:Decision 3 棋盘渲染形态条款由 [ADR-0006](adr-0006-board-node2d-rendering.md) 部分取代——棋盘本体定向迁 Node2D 渲染(D 区 Phase A 先行,B 区舞台后续批),「禁盲目迁移」条款解除,其登记的探针耦合成本转为迁移批义务(实证收敛为 `_cells` 键面 + `click_cell` + `cell_size` 三点,公共面契约保持即可零改造)。Alternative 2(逐格 Node2D)的否决理由「无必要」因表现层升级需求不再成立;autoload 清单、类形态、CJK 字体前置等其余条款不变。
## Date
2026-09-12
## Engine Compatibility
| Field | Value |
|-------|-------|
| **Engine** | Godot 4.7.2-stable |
| **Domain** | Core / Scripting + UI |
| **Knowledge Risk** | LOW-MEDIUM(结构决策 LOW;渲染呈现项 MEDIUM——CanvasItem 线条 AA 羽化移除已核实,godot-specialist 校验含 2 项实质修正后通过) |
| **References Consulted** | `docs/engine-reference/godot/`(四文件);godotengine/godot#6360(--script 无 autoload) |
| **Post-Cutoff APIs Used** | None |
| **Verification Required** | CJK 字体随包发行的显示核对(首次正式导出 checklist 增补项);D1 正式化时若执行 _draw 迁移则另核命中测试/无障碍 |
## ADR Dependencies
| Field | Value |
|-------|-------|
| **Depends On** | ADR-0001(组合根接线纪律)、ADR-0002(project.godot 基线同批落盘) |
| **Enables** | 正式工程骨架搭建;D1 正式化 |
| **Blocks** | ADR-0004 扫描域(引用本 ADR 的 core 隔离规则) |
| **Ordering Note** | 棋盘渲染形态裁决(本 ADR §Decision 3)与 ADR-0004 探针耦合——见 Decision 3 的迁移工作项 |
## Context
### Problem Statement
正式工程骨架需要定稿:autoload 清单、core/presentation 的类形态与目录结构、棋盘渲染的实现路径。切片已验证一套形态,但初稿 ADR 把"自绘 _draw()"当作切片事实——godot-specialist 校验发现切片 BoardView 实为 **GridContainer + 逐格常驻控件**,本 ADR 按校验修正。
### Constraints
- Core 层零 UI 依赖、headless 可跑(D2 Rule 7);--script 模式下 core 禁裸引用 autoload 全局标识符(编译期即炸——见 ADR-0004)
- 同屏 draw calls < 200;棋盘 ≤12×12(MVP 量级)+数字/小人/旗/变异多层叠加(视觉锚绘制优先级栈)
- 领域命名规范(docs/code-conventions.md:禁 ID 前缀、头注溯源)
### Requirements
- 切片验证过的结构不推倒(用户拍板"按常规老路子")
- 呈现层与核心的写边界:核心写调用唯一发生在组合根
- 中文文案可显示(运行时默认字体无 CJK 字形——specialist 核实)
## Decision
**1. autoload 清单 = {ConfigDB, RngService}(定名锁定),仅此两个。**
战斗编排器等核心类为普通类、由组合根按局实例化——非 autoload、非场景节点。
**2. 类形态与目录结构:core/ 纯逻辑类 = RefCounted;ConfigDB/RngService = autoload Node 单例(引擎约束:autoload 脚本必须 extends Node);presentation/ = Control 派生视图单元。**
```
res://
├── core/ # RefCounted 纯逻辑(board_generator / minesweeper_rules /
│ # action_economy / battle_orchestrator / unit_container /
│ # status_container / reward_economy / level_runner / 数据类)
│ # —— 零 Control 引用、零 .tscn preload、零 autoload 全局标识符裸引用
├── data/
│ ├── schemas/ # D2 schema 类(Resource 派生——D2 体系,非本 ADR 变更项)
│ └── tables/ # .tres 数值表(单一事实源)
├── presentation/
│ ├── views/ # 九单元:BoardView / HUDView / BattleLogView / ItemWindowOverlay /
│ │ # NodeOverlay / LevelStripView / TunePanel / UiKit
│ ├── hud_presenter.gd
│ └── main.tscn + main.gd # 组合根:模式流转 + 信号接线 + 核心写调用唯一落点
├── smoke/ # 冒烟脚本 + 探针场景(ADR-0004)
└── docs/architecture/
```
**3. 棋盘渲染形态:MVP 沿用切片已验证形态——GridContainer + 逐格常驻控件(BoardView 持 `_cells`,refresh 内逐格更新);自绘 `_draw()` 迁移登记为 D1 正式化候选工作项(非本 ADR 承诺)。**
- specialist 校验事实:切片 BoardView = GridContainer + 逐格 Button(`_paint_cell` 逐格更新),三探针与该形态耦合(ui_probe 断言 `_cells` 非空、click_probe 依赖 `click_cell` 入口)。初稿"_draw 是切片形态的正式化"**不成立**,按事实修正。
- 形态对比留档:逐格控件(现状)= 命中测试/悬停/焦点由引擎承担、探针兼容;代价 = 控件树随格数增长(12×12=144 控件,实测无压力)。`_draw()` = 单 draw call、绘制优先级栈完全可控;代价 = 自建命中测试/悬停 + **三个探针改造**(`_cells` 断言与 `click_cell` 入口失效)+ 无障碍面缺失 + 需补 `Viewport.push_input` 端到端输入探针。
- 裁决:D1 正式化 GDD 评审时按当时视觉需求(biome 色板、雾纹理、动效复杂度)二选一;在此之前禁盲目迁移(探针改造是隐藏成本)。任一形态下 CanvasItem 线条 AA 羽化移除(4.7)需注意——格边框用填充 `draw_rect` 不受影响。
**4. CJK 字体前置(specialist 补登,两种形态都受影响)**:Godot 运行时默认字体不含 CJK 字形——全部中文文案(按钮/标签/结算明细)必须**随包发行 CJK 字体并设为 theme 默认字体**。登记为 D1 正式化硬性前置项 + 首次导出 checklist 项。
### Architecture Diagram
见 `docs/architecture/architecture.md` System Layer Map + Module Ownership(本 ADR 是其工程形态落点)。
### Key Interfaces
- 组合根 = 唯一核心写调用落点(settle_batch / submit_window_selection / runner 推进)——切片纪律转正
- 视图单元 = 信号外发 + setter 只读注入(BoardView 只读 A2、动作信号外发不碰核心写)
## Alternatives Considered
### Alternative 1: 战斗编排器设为 autoload
- **Cons**: 编排器持有跨局状态却永生——生命周期与战斗域契约冲突(Uninit/BattleOver 状态机要求按局重建);与"autoload = 全局基础设施"边界冲突。
- **Rejection Reason**: 切片形态(按局实例化)已验证且语义正确。
### Alternative 2: 棋盘 = 每格一个 Node2D
- **Pros**: 独立节点动画/模组方便。
- **Cons**: 每节点处理开销与 Control/Node2D 混管复杂度;无必要(specialist 指出 2D 同材质有批处理,draw call 论据最弱,但节点开销与混管论据成立)。
- **Rejection Reason**: 逐格控件已覆盖需求且已验证。
### Alternative 3: TileMapLayer 承载棋盘
- **Cons**: 4.5 物理分块默认变更(纯视觉不踩,但物理用途即触);瓦片语义与"变体标记/数字/旗多层叠加"不匹配。
- **Rejection Reason**: 语义不匹配是硬伤;现形态无此问题。
## Consequences
### Positive
- 结构与切片零迁移成本;core 隔离规则可静态扫描(ADR-0004)
- 棋盘形态留有 D1 期复议空间且复议成本已如实登记
### Negative
- 逐格控件形态在 D1 需要高表现力时可能仍需迁移 _draw(成本已登记:探针改造 + 命中测试自建 + push_input 端到端探针)
- CJK 字体为新增资产义务(发行体积 +)
### Risks
- D1 正式化时遗漏"探针与形态耦合"约束 → 缓解:本 ADR Decision 3 + ADR-0004 探针节双登记
- core 类被随手加 autoload 裸引用 → 缓解:ADR-0004 静态扫描(parse 期即炸的事实使该风险自暴露)
## GDD Requirements Addressed
| GDD System | Requirement | How This ADR Addresses It |
|------------|-------------|--------------------------|
| d2-data-config.md | Rule 7 零 UI 依赖;Rule 2 单点访问 | core 隔离规则 + autoload 清单固化(ConfigDB/RngService 定名) |
| game-concept.md | 视觉锚(绘制优先级栈、双通道) | 棋盘形态两案对比留档,D1 正式化按锚需求裁决 |
| b1-settlement.md | Core Rule 1 编排器按局生命周期 | 编排器非 autoload、组合根按局实例化 |
| docs/code-conventions.md | 领域命名 / 组合根纪律 | 目录结构与 Key Interfaces 转正 |
## Performance Implications
- **CPU**: 144 常驻控件逐格更新——切片实测无压力;_draw 迁移后更优(单 draw call)
- **Memory**: 可忽略
- **Load Time**: CJK 字体载入一次性成本
- **Network**: 不适用
## Migration Plan
切片即目标形态(结构零迁移);CJK 字体接入与 _draw 复议归 D1 正式化批次。
## Validation Criteria
- 冒烟 88/88 + 三探针在正式工程维持全绿
- 静态扫描:core/ 零 Control extends / 零 .tscn preload / 零 autoload 全局标识符裸引用(ADR-0004 域)
- 中文文案在导出包内显示正常(导出 checklist)
## Related Decisions
- ADR-0001(接线纪律)、ADR-0002(渲染器与窗口键)、ADR-0004(扫描域与探针)
- docs/architecture/architecture.md(Module Ownership / Required ADRs——本 ADR 落定后其 §Required ADRs 第 3 项闭合)