Files
gplan-2/docs/architecture/board-node2d-migration-plan.md
T

103 lines
9.5 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.
# 战斗棋盘 Node2D 渲染迁移方案(Phase A:仅棋盘)
> 状态:**草稿待审**(2026-09-23 起草,board-node2d-plan-20260923 worktree)。
> 上位裁决:ADR-0006(分工与形态)、用户 2026-09-23 拍板「Node2D 重构只迁战斗棋盘,开始菜单/各覆盖层保持 Control;战斗画面 Node 后续批完成」。
> 本文档是 Phase A 实施批的工程方案;批准后实施批从本文开工,不再另起炉灶。
## 0. 一句话
把 D 区棋盘本体从「GridContainer + 逐格 Button」换成「Control 壳 + SubViewport + Node2D 世界」,BoardView 类名与公共面契约逐项保持——探针与组合根零改造,素材终稿到位后 1:1 换图零返工。
## 1. 目标形态(已与用户确认的三块模型)
战斗屏 = 两块 Node2D 舞台 + 一层 Control 面板。术语校准:Godot 没有「Canvas」节点——面板层是 Control 树(常驻顶层与弹层经 CanvasLayer,现状已如此);Node2D 不参与 Control 容器布局,所以每块舞台包一个 Control 壳(SubViewportContainer)参与屏幕栅格布局,壳内才是 Node2D 世界。
```
BattleScreen(Control 栅格;目标形态 = B/C/D 三区——进度条退场裁定见下)
├── B 战斗画面(Phase B 改造,本批不动)
│ ├── Node2D 舞台壳:主角/敌人站桩、意图动画、演出(后续批)
│ └── Control 半区:结算明细带、回合指示、战斗日志(永久 Control)
├── C 角色信息 .................. Control(不动)
└── D 扫雷图
├── AP 条、按钮组、toast .... Control(不动)
└── BoardView(本批改造对象)
└── SubViewportContainer(Control 壳,参与布局)
└── SubViewport(逻辑分辨率 = 格边长 × 板尺寸)
└── BoardWorld(Node2D)
└── CellUnit × N(Node2D,每格一个)
```
**进度条退场裁定(2026-09-23 用户)**:战斗画面不再有关卡进度条——战斗结束后返回关卡画面,节点链进度只留关卡画面。
**A 区代码命运(2026-09-23 追问裁定):退役,不迁移。关卡画面不重新实现。**
- **关卡画面 = RouteOverlay 升级**:现役选路覆盖层已是整幅节点图 + 全套节点态语义(暗金已过/金当前/暗未达/亮金候选通道/旗朱 Boss 边,与压缩条同一组 UiKit token)+ 候选按钮 + 放弃入口。停点前移批只改开合时机(关卡开始即开、战后返回即开)与流程接线(选路确认不再经兑付停点绕行),渲染本体零重写。
- **LevelStripView 不迁移的理由**:它唯一的存在理由 = 战斗屏上看不到整图时的 28px 压缩常显替代面。地图页上压缩条冗余(整图即进度),战斗屏又已裁定零进度显示——迁过去没有消费者。其语义无独有资产(色板/节点态与 RouteOverlay 同族),单链退化渲染(全链横条)被整图布局天然取代。
- **唯一继承面 = 逻辑层数据面**:runner 只读出口(`nodes()` / `visited_ids()` / 节点编号空间 / `next_candidates()`)零改动——压缩条与整图本来就消费同一份数据,这正是"退役不迁移"成立的根据。
排程事实与修订面:
1. **A 区现状代码继续服役**:当前流程尚未倒置(冒险开局直进战斗,节点图是战后弹层),A 区顶栏是战斗过程中唯一的节点链可视面——先删会致盲。退场落地 = 停点前移批,届时四区→三区。
2. **停点前移批的删除面清单**(退役随该批执行):`LevelStripView` 类、ui_assembler 组装行(:28)、main.gd `strip_view` 引用(:52 等)、ui_probe 两处断言(:77–78 节点条构建检查、:188 报告行)、battle.md A 区条款。
3. **该批的 GDD 修订面**:HUD框架.md 四区结构表(含 B/C/D 是否重新编字母)、关卡节点地图.md 节点链常显条款(常显载体随进度条退役,由关卡画面本体承接)、battle.md 像素布局表(28px 纵向空间再分配)。
## 2. Phase A 范围(本批做)
1. **BoardView 内部重构**(`presentation/views/board_view.gd`):
- 根仍是 Control(类名/extends 不变),子结构 GridContainer → SubViewportContainer + SubViewport + BoardWorld。
- `_cells`:Vector2i → CellUnit(Node2D)字典,键面与现 Dict 同构(探针断言面,见 §4)。
- CellUnit 内部固定叠层序:砖面底 < 变异纹带 < 数字/徽章 < 旗 < 焦点高亮——本批全程序化绘制(`_draw`),等价平移现行视觉;每个叠层留 `texture` 槽位(§8.2 暂定档 32×32),终稿纹理换入即用。
- 现有行为逐项平移:呼吸微光(StyleBoxFlat 边框 → `_draw` 边框)、悬停反白、路径预览、危险红、双击 chord 检测、`set_desaturated`(ShaderMaterial 挂 BoardWorld 材质)。
2. **输入换板**:BoardView 壳层 `_gui_input` 接鼠标 → 格坐标单点公式 `floor(local_pos / cell_edge)` → 复用现有 hover/click/chord/长按逻辑;`action_requested` 信号链不变。
3. **`cell_size` 只读属性增补**:现私有名 `_cell`,ui_layout_probe `get("cell_size")` 读到 null——补只读属性,顺手修正。
4. **push_input 端到端输入探针**(新增,ADR-0003 登记项转义务):合成鼠标事件经真实输入路径打 SubViewportContainer,验悬停/左键/右键/chord 四 case——现探针全走 `click_cell` 直调,公式换算无覆盖,这是唯一新增自动化面。
5. **冒烟全量回归 + 文档随批**:HUD框架.md 渲染技术注记(v0.x 版本行)、battle.md 实现注记(格尺寸公式锚点不动)、architecture.md BoardView 形态行、ui_kit 同步面清点。
**不做**(明确出批):B 区舞台任何改动;素材终稿接入;格边长数值调整(归样张批);键盘寻格/无障碍(沿 ADR-0003 已接受口径)。
## 3. Phase B 预留(后续批,本批只保证不堵死)
- BattleStageView 同构改造:PanelContainer → Control 壳(Node2D 舞台 + Control 文本半区),站桩 StandeeView 从 Control 迁 Node2D 精灵——意图三档动画/受击/死亡演出的载体。
- 本批交付的两个可复用挂点:SubViewport 内 theme/字体显式注入挂点(CJK 字体义务,ADR-0003 Decision 4 关联)、CellUnit 叠层槽位模式(B 区站桩底座同构)。
- Phase B 开工硬前置 = 样张批冻结剪影与动画方案(清单 §1.2 联动条款),与本批无相互阻塞。
## 4. 探针与测量面(实证清单)
| 探针/工具 | 耦合点(实证) | Phase A 处置 |
|---|---|---|
| ui_probe | `board_view._cells.is_empty()`(:73)、`_cells.size()` 对板宽高(:187)、`board()`/`set_desaturated()` | `_cells` 键面同构即绿,零改 |
| ui_click_probe | `click_cell(cell, button)` 直调(:257–277)、`_cells.is_empty()`(:97);**无合成鼠标事件**(grep parse_input_event/push_input 零命中) | 公共方法保留即绿,零改 |
| ui_layout_probe | `get("cell_size")`(:39,现读 null)、`board()` | 增补只读属性(§2.3) |
| smoke 行协议同步闸 | 战斗日志 9 类行格式化(smoke_all.gd:3325+)——**是日志行不是棋盘** | 零影响,全量回归覆盖 |
| MC / 模拟试玩 / verifier | core/ 逻辑层 | 零接触零影响 |
## 5. 性能与预算
- draw calls:CellUnit 同材质 `_draw` 走 2D 批处理,144 格占位态预计个位数批次;预算 < 200 维持(HUD框架 引用值),实施批用 RenderingServer 实测数回填。
- 进入战斗屏 500ms 可交互:SubViewport 创建在 `_ready` 一次完成,无逐帧重建;`set_cell_area`/`rebuild` 只重排子节点。
- SubViewport 逻辑分辨率 = 格边长 × 板尺寸(≤ 36px × 14 格量级),渲染目标显存占用可忽略;低端机帧预算实施批实测。
## 6. 风险与对策
| 风险 | 对策 |
|---|---|
| SubViewport 内 Label 不继承主场景主题(数字/中文渲染空白或字体错) | 实施批第一小步先做 theme/字体显式注入挂点并用 CJK 冒烟验证——ADR-0006 Verification Required 项 |
| 45/36px 锚点 × 32px 格源非整倍缩放出现像素纹路 | 架构只保证 `set_cell_area` 单点可调;数值归样张批(圣经 §8.2 v1.9 注记①路线),本批不裁决 |
| `_gui_input` 与 toast/AP 条控件输入抢占 | D 区内部布局不变(Control 兄弟件照旧),棋盘壳是叶节点,抢占面与现 GridContainer 相同;探针四 case 覆盖 |
| 改造期行为漂移(呼吸微光/chord 时序等隐性手感) | 功能等价清单(ADR-0006 Validation)逐项人工核对 + 探针自动化面 |
## 7. 排期与依赖
| 项 | 估时 | 依赖 |
|---|---|---|
| 本起草批(ADR-0006 + 本方案 + ADR-0003 注记) | 已完成,待审 | 无 |
| Phase A 实施批 | 1–2 天(含探针增补 + 冒烟回归 + 文档随批) | 本方案批准 |
| 样张批(素材侧,另线) | 未排 | 主角候选表态 + 复用批审阅 |
| Phase B 实施批 | 未排(同构复制,估时随 Phase A 实测修正) | Phase A 落地 + 样张批冻结 |
与在飞批的关系:曲线调参双刀扫描(worktree hp-bonus-scan-20260923)与本批文件集零交集;UI 复用候选审阅是素材侧独立线,互不阻塞。
## 8. 验收(实施批完成定义)
ADR-0006 Validation Criteria 全项:冒烟全量绿 + 三探针绿 + 新增 push_input 探针绿 + 功能等价清单逐项可验 + draw calls < 200 实测回填 + 两档分辨率装板校验(12×12 与 10×10 各验)。