19 KiB
Dungeon Sweeper — 总体架构文档
文档状态
- 版本:3.0(依赖规则形态——分层/目录归属/数据流/原则;模块级归属登记与模块对接口契约均不进本文:前者归代码文件头注,后者规范源单点=各系统 GDD、执法=静态扫描域与契约测试)
- 引擎:Godot 4.7.2-stable(GDScript 单语言;2026-09-07 锁定)
- 评审模式:solo(
review-mode.txt——门禁按 solo 跳过) - 覆盖 GDD:现行全集(根索引=
design/gdd/02-系统清单.md,文档树构成以根索引为准) - 事实源地位:本文是架构约束的唯一权威(分层/目录归属/数据流/架构原则);写权限=用户专属(agent 与技能只读)
- 实现实证:
game/(完整游戏开发版本;本文分层与所有权经实现验证)。测试层级=冒烟/单元/系统三级+UI 探针与渲染 golden,识别三元组权威=design/engineering/test-conventions.md「测试层三元组」节
引擎知识缺口摘要
- 引擎事实现行事实源=
design/engineering/engine-reference/godot/(VERSION / breaking-changes / deprecated-apis / current-best-practices 四件)——迁移核实结论、语义变化与编码注意一律查那里,本文不重复。 - 渲染器与窗口基座=gl_compatibility + 1280×800;UI/渲染域为引擎风险集中地,采纳引擎增量前对照 engine-reference 核实。
- 写码纪律不变:不确定的 API 一律查
docs.godotengine.org/en/4.7/核实后再用。
系统分层图
20 系统按 design/gdd/02-系统清单.md 依赖分层表映射到标准五层。实现已验证该分层(core/ 纯逻辑零 UI 依赖 headless 可跑、presentation/ 组合根模式)。
┌────────────────────────────────────────────────────────────┐
│ 呈现层 │
│ D1 HUD 与界面框架 (MVP) · B5 战斗表现 │
│ D4 音频 (Beta) · D5 教学引导 (Beta) │
├────────────────────────────────────────────────────────────┤
│ 功能层 │
│ B1 战斗结算编排 (MVP) · C1 关卡节点地图 │
│ C2 冒险结构 · C3 难度曲线 · C4 元进度 (Alpha) │
├────────────────────────────────────────────────────────────┤
│ 核心层 │
│ A1 扫雷棋盘生成 · A2 排雷玩法规则 · B2 敌人系统 │
│ B4 地图战斗资源 · A5 扫雷秘宝 │
├────────────────────────────────────────────────────────────┤
│ 基础层 │
│ D2 数据配置与数值表 · A3 行动经济 · A4 地形变异 │
│ B3 增益/状态系统 · D3 存档序列化 (Alpha) · D6 设置/本地化│
├────────────────────────────────────────────────────────────┤
│ 平台层 │
│ Godot 4.7.2 引擎 API 面(GDScript / Resource / │
│ SceneTree / Control / InputEvent)——无游戏系统 │
└────────────────────────────────────────────────────────────┘
层名对照:基础层=Foundation、核心层=Core、功能层=Feature、呈现层=Presentation、平台层=Platform——本文一律用中文名;跨文档与代码目录仍见英文名(
game/core/、game/presentation/等目录名不改)。
| 系统 | 层 | 里程碑 | headless 约束 | 引擎风险域 |
|---|---|---|---|---|
| D2 数据配置 | 基础层 | MVP | ✅ 完整可跑 | LOW(Resource/.tres 基线稳定) |
| A3 行动经济 | 基础层 | MVP | ✅ 完整可跑 | LOW(纯逻辑) |
| A4 地形变异 | 基础层 | MVP | ✅ 完整可跑 | LOW(纯逻辑) |
| B3 状态系统 | 基础层 | MVP | ✅ 完整可跑 | LOW(RefCounted 纯逻辑) |
| A1 棋盘生成 | 核心层 | MVP | ✅ 完整可跑 | LOW(纯逻辑) |
| A2 排雷规则 | 核心层 | MVP | ✅ 完整可跑 | LOW(零表读取零 UI) |
| B2 敌人系统 | 核心层 | MVP | ✅ 完整可跑 | LOW(纯逻辑) |
| B4 地图战斗资源 | 核心层 | MVP | ✅ 完整可跑 | LOW(纯逻辑) |
| B1 战斗结算编排 | 功能层 | MVP | ✅ 完整可跑(禁 await) | LOW(纯逻辑) |
| C1 关卡节点地图 | 功能层 | MVP | ✅ | LOW |
| C2 冒险结构 | 功能层 | MVP | ✅ | LOW |
| C3 难度曲线 | 功能层 | MVP | ✅ | LOW |
| C4 元进度 | 功能层 | Alpha | ✅ | LOW |
| D1 HUD | 呈现层 | MVP | ❌(UI 层——场景级探针测试承载) | MEDIUM(Control/Input/文本域) |
| B5 战斗表现 | 呈现层 | 切片后立项(未实装,前置战斗表现美术测试) | ❌ | MEDIUM(Animation/Tween/渲染) |
| D4 音频 / D5 教学 | 呈现层 | Beta(与游戏内容无关者一律排 Beta) | 部分 | LOW-MEDIUM |
| D3 存档序列化 | 基础层 | Alpha | ✅ | LOW |
| D6 设置/本地化 | 基础层 | 完整愿景 | ✅ | LOW |
依赖分层与唯一回边:A2 只外发三事件(cell_revealed / flag_judged / round_end),B4/B1 订阅后回调——A2 不依赖任何下游(TD-SYSTEM-BOUNDARY 裁决,全项目唯一允许回边)。
目录归属
目录=层级的物理承载:同一目录内的文件同层级、同受该层约束属性管辖,职责各自独立、归各文件头注(
##文档注释)承载;领域不做目录切分,仅当领域大到内部再需要一层时才立子目录。命名遵循design/engineering/code-conventions.md:领域命名、禁 ID 前缀。逻辑依赖层级(基础/核心/功能/呈现)见「系统分层图」——game/core/一目录内按依赖层级再分三段,层级约束由分层图与架构原则承载,目录不再切分。
| 目录 | 层级作用 | 该层约束属性 |
|---|---|---|
game/core/ |
全部玩法逻辑的承载(配置、RNG、存档域件、棋盘生成、排雷规则、经济与账本、效果引擎、战斗编排、关卡与冒险域件)——依赖逻辑层级内分基础/核心/功能三段,见分层图 | 纯逻辑:零 Control/场景引用、构造注入、fail-fast、--headless --script 完整可跑、玩法数值零字面量(读 game/data/tables)、随机只经封闭流 |
game/core/settlement/ |
领域子目录——战斗结算五段段件(领域大到内部再需要一层时切分的在案先例) | 同 core;段类可直接 new() 单测、段类对编排器零引用 |
game/presentation/ |
呈现层——全部 Control/CanvasLayer、输入→意图翻译、组合根装配 | 视图零核心写调用(信号外发为主)、组合根=核心写调用唯一出口、呈现数据只走成文快照面;引擎风险域对照 breaking-changes |
game/data/ |
数据承载——玩法数值表(tables/.tres)与字段契约(schemas/.gd),对应基础层 D2 数据配置 | 数值单一事实源;表值直接改变游戏运行结果 |
game/tests/ |
测试域——冒烟/单元/系统三闸承载 | 依赖只经 autoload 边界与场景契约两类入口;禁反向依赖呈现层实现内部 |
game/probe/ |
探针域——场景级探针与取证探针 | 同测试域边界;在闸件与手动测量件两分 |
game/sim/ |
测量域——MC/调参/数值验证驱动件 | 不入 CI、用户手动发起、随机显式定种 |
依赖单向性:上层可消费下层,禁止反向(C3 禁调用 A1 生成、D1 不直调 A3、A2/B3/B4 零相互直调)。全项目唯一回边=A2 事件订阅面。
呈现层装配与会话生命周期
三屏壳与组合根
- 呈现场景拆为三屏壳:MenuScreen(开始菜单屏)/CampScreen(营地屏)/AdventureScreen(冒险屏——内含关卡地图与战斗两容器)。
- 屏成员寻址经界面注册面(ui_registry):探针与消费方走
main.ui.<域容器>.<句柄>合同寻址;句柄清单与合同细节归 ui_registry 头注承载。 - 局态一律住 RunScope 节点,禁在屏/视图侧持有局态引用。
视觉层装配合同
- 树序=登记序=z 序,同源派生;层序唯一事实源=
main.gd的LAYER_CONTRACT常量表(kind 五类:shell/modal/state/node/float)。 - 全仓唯一落位口=
_apply_layer_contract()(装配尾统一排序+登记);散落move_child由冒烟源码断言判红;改层序只改合同表。 - 具体层清单不复制进本文(双写失同步禁令)——查现行层项直接读
main.gd。
会话生命周期(RunScope)
run_scope.gd=纯状态承载 Node:零信号连接、零视图构建、零核心写调用(连接只在组合根,组合根经run_scope.X读写)。- 会话更替机制:
_begin_session()新建会话节点挂树、旧会话节点整节点queue_free()——无独立收束函数,跨局字段清理靠节点释放机制。 - 存活性矩阵(九阶段 × 五引用面;执行判据=系统测试 V3a–V3i):
| 阶段 | run_scope | adventure | runner | settlement |
|---|---|---|---|---|
| ① 闲时 | 在树(闲时会话) | null | null | null |
| ② 开局停点 | 在树 | alive | alive | null |
| ③ 战斗中 | 在树 | alive | alive | alive |
| ④ 结算后(胜利停点)/⑤ 选路页 | 在树 | alive | alive | both |
| ⑥ 终局面 | 在树 | alive | alive | null |
| ⑦ 回菜单收束 | 新闲时会话即刻在树(旧会话已释放——无真空窗) | null | null | null |
| ⑧ 各模态开合后 | 闲时会话零扰动 | null | null | null |
| ⑨ 两连局+续玩断点 | 换全新会话在树 | alive | both | both |
- 三态记号:alive=应有实例、null=必须缺位(实测非空判红)、both=按分支可空可活。对象有值但已失效一律判「悬挂」红。
模态编排(ModalFlow/ModalStack)
modal_flow.gd=模态编排纯逻辑类:开合编排(遮挡记账单口+演播后排窗挂起)/棋盘输入锁谓词/角标置灰谓词三职责;可直接new()单测。- 互斥排除语义随层登记(
register(node, tags));其余层零标签。
测试与质量域
定位:本节只写测试域的架构事实。测试工具手册与各闸判定口径的事实源=
game/tests/README.md;闸清单与改动域划分的唯一出处=tools/run_all_gates.sh(脚本即清单);测试怎么写、怎么算合格=design/engineering/test-conventions.md;静态扫描域 S 系规则的规则全集与动机=game/tests/README.md「Static Scan」节。全量验证=合回后远端 CI;开发期义务=改完即测(process-conventions §4)。
测试域依赖约束
测试域禁止反向依赖呈现层具体实现。五闸及其调用的探针场景、审计脚本只允许依赖两类边界:
- autoload 边界——ConfigDB / RngService 的公开面(--script 模式不加载 autoload 且不注册全局标识符,测试文件自带显式实例化夹具);
- 场景契约——探针场景的信号外发面与视图单元接口(信号外发、setter 只读注入),禁直引呈现层实现内部。
测试域是所有层的消费方,进入方式限于上述两类边界;除此之外的依赖方向一律与「模块归属」节依赖单向性同规。
数据流
1. 帧更新路径(2D 回合制:无逐帧玩法逻辑)
Input(鼠标)→ D1 输入翻译 → 意图对象 → A2.settle_batch(唯一提交入口)
→ A3.try_spend(原子扣减)→ A2 棋盘效果(挖/连锁/封印/移动)→ 三事件外发
→ [棋盘渲染:D1 BoardView 按事件增量重绘;无事件帧 = 静态 + 动效]
→ 轮次结束 → A3 Waiting → B1 结算编排(段序七段)→ 明细步骤流 → D1 结算屏逐行呈现
- 回合制下每帧只做:输入采样、呈现刷新、动效推进——玩法状态只在意图事务内变更(单意图帧输入收集:stop 点击终结当帧)。
- 无跨线程玩法数据流(Godot 主线程假设);A1 生成若引入 WorkerThread,join 必须先于任何快照切出(既定约束)。
2. 事件/信号路径(系统间通信,无紧耦合)
A2 ──cell_revealed / flag_judged / round_end(棋盘路径)──▶ B1(轮 facts 缓冲)
A3 ──round_end(stopped 直发,payload 同构)──────────────▶ B1
B1 ──notify_settlement_done(next_round_mode) / notify_combat_end──▶ A3
B1 ──apply_effect / apply_damage / grant / 伤害单据 submit…──▶ B3/B2/B4(唯一调用方)
B1 ──install_board / retire_board──▶ A2 B1 ──generate(L,D)──▶ A1
D1 ◀──呈现数据源:A2 事件流 / B1 步骤流 / A3 ap-rap / ConfigDB display_name
- 事件载体(已定):组合根直连信号接线 + B1 Waiting 相位触发(不依赖事件到达序);autoload 全局事件总线已否决(2026-09-12,沿革见 git 历史)——单线程回合制沿用组合根直连(KISS)。选型理由=可回放(架构原则 3):同步直连让调用顺序显式可见,「同种子逐位相同」才可断言;事件驱动形态服务的是并发/IO 场景对外部不确定时序的反应性,与本作「顺序确定」的需求相反。
- 事件规范序:A2 发射序=触发格 BFS、8 邻行主序;事件序契约
flag_judged<round_end全路径统一。 - 呈现层订阅禁令(切片缺陷教训固化):呈现层永不直接订阅排雷玩法/行动经济的事件流,只订战斗结算步骤流与
level_runner——核心事件形态变化不得直接冲击界面。
3. 存档/读档路径
- 存档系统已实装:格式、三槽位、断点域与迁移链的现行契约以
design/gdd/systems/表现与支撑/存档序列化.md(owner)为准。 - RNG 快照契约(约束,现行成立):断点续玩回填=master_seed + 各流 scope 计数器;快照切出须发生在无正在执行中的 open_stream 实例时(scope 事件边界外——切点=board install 边界);SAVE_FORMAT_VERSION 高版本拒读。
4. 打包与字体(CJK 随包发行)
CJK 字体随包发行并设为 theme 默认字体——运行时默认字体无中文字形。此项是 HUD 框架正式化的硬性前置,同时进导出 checklist 核对项。
5. 初始化顺序
进程启动
→ autoload 实例化:ConfigDB → RngService → SaveManager → MetaProgression(顺序固定;核心类为普通类、由组合根按局实例化)
→ ConfigDB._ready():load_tables 加载 + 校验(致命通道;失败 = 构建中止 + 错误清单)
→ 主场景(D1 组合根)装配视图单元 + 信号接线
→ C2 begin_run:RngService.set_master_seed()(唯一接线方;两流计数器全零前提)
→ B1.begin_battle(encounter):set_reload_gate(true) → B3 开域 → B2 spawn → A2 install_board(A1.generate) + B4 roll_board(恰一次)→ A3 notify_round_start
→ 挖掘循环 …… → battle_over 清理序列 → set_reload_gate(false)
→ 关卡末:通关结算(钥石/GP/专长 level_clear 渠道——现行语义归 冒险结构 GDD)
启动后置条件(执行判据事实源在 game/tests/system_test.gd):
- ConfigDB 就绪且表已载(
is_ready+ hero 表在座)。 - 系统设置已落引擎态——Master 音量 mute/volume_db=settings_store 现值;减动效开关=
UiKit.motion_scale。 - 启动态菜单可见(boot 后自树顶向下第一个可见内容层=开始层)。
- 续玩钮可见态恒等于断点在档态(双向)。
- 闲时会话已在树(
run_scope永非 null 契约的启动侧证明)。 - 调试浮窗 boot 不自动开(菜单优先)+调试入口全局浮层角标 boot 可见。
架构原则
- 核心层纯逻辑、headless 完整可跑——核心层/功能层玩法逻辑零 Control/场景引用、零 UI 依赖;
--headless --script可跑完整战斗。输入是意图对象、输出是事件流/步骤流,D1 与输入设备皆可替换。 - 单一事实源与零字面量——玩法数值只存在于
data/tables/*.tres,经 ConfigDB 类型化单点读取;代码零玩法数值字面量;参数禁止跨系统复制(D2 Rule 1/2)。 - 封闭 RNG 与可回放——玩法代码零随机;一切随机经 RngService 具名封闭流(增流走 P1 评审);同种子 + 同输入序列 ⇒ 事件流/步骤流逐位相同(结算禁 await,B1 承重约束)。
- 编排唯一性与事件面封闭——战斗域跨系统调用权集中 B1(静态扫描承载);A2 三事件面封闭即负空间;每格受理、每雷转化、每次推进、每次衰减各恰一次。
- fail-fast + 零特判——数据错误一律致命中止(错误数值上线比崩溃更伤);API 非法=拒绝 + 诊断不静默;变异零运行时分支(行为差异全部由生成端约束保证)。
- 依赖单向 + 组合根模式——层间单向依赖、唯一回边=A2 事件订阅面;核心写调用唯一发生在组合根;禁上帝对象。
- 呈现层契约成文——面向呈现的信号载荷必须有成文 schema;模态覆盖层必须有"打开时遮挡物隐藏"场景级断言;场景级 UI 探针测试为标配(切片缺陷教训沉淀)。
- 系统无状态——效果引擎这类系统零运行时可变状态:数据由宿主对象持有并随宿主生灭,状态按参数传入系统,系统只做判定。系统实例不持计数器——计数器住桶(round/battle/level/adventure 四桶,EffectContext 引用束),桶住宿主。
- 停点权威在核心层——玩家决策停点(选路、三选一、商店、恢复这类)的推进守卫住核心层状态机:停点未兑付,推进口一律拒绝(零自动推进契约);呈现层模态只做开合与输入锁——阻塞是两层结构,核心守卫为权威、模态为体验;停点态是核心状态、随断点序列化,headless 跑批与真实局同一套规则,中断续玩恢复到同一停点。