10 KiB
战斗棋盘 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())零改动——压缩条与整图本来就消费同一份数据,这正是"退役不迁移"成立的根据。
排程事实与修订面:
- A 区现状代码继续服役:当前流程尚未倒置(冒险开局直进战斗,节点图是战后弹层),A 区顶栏是战斗过程中唯一的节点链可视面——先删会致盲。退场落地 = 停点前移批,届时四区→三区。
- 停点前移批的删除面清单(退役随该批执行):
LevelStripView类、ui_assembler 组装行(:28)、main.gdstrip_view引用(:52 等)、ui_probe 两处断言(:77–78 节点条构建检查、:188 报告行)、battle.md A 区条款。 - 该批的 GDD 修订面:HUD框架.md 四区结构表(含 B/C/D 是否重新编字母)、关卡节点地图.md 节点链常显条款(常显载体随进度条退役,由关卡画面本体承接)、battle.md 像素布局表(28px 纵向空间再分配)。
2. Phase A 范围(本批做)
- 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 材质)。
- 输入换板:BoardView 壳层
_gui_input接鼠标 → 格坐标单点公式floor(local_pos / cell_edge)→ 复用现有 hover/click/chord/长按逻辑;action_requested信号链不变。 cell_size只读属性增补:现私有名_cell,ui_layout_probeget("cell_size")读到 null——补只读属性,顺手修正。- push_input 端到端输入探针(新增,ADR-0003 登记项转义务):合成鼠标事件经真实输入路径打 SubViewportContainer,验悬停/左键/右键/chord 四 case——现探针全走
click_cell直调,公式换算无覆盖,这是唯一新增自动化面。 - 冒烟全量回归 + 文档随批:HUD框架.md 渲染技术注记(v0.x 版本行)、battle.md 实现注记(格尺寸公式锚点不动)、architecture.md BoardView 形态行、ui_kit 同步面清点。
不做(明确出批):B 区舞台任何改动;素材终稿接入;格边长数值调整(归样张批);键盘寻格/无障碍(沿 ADR-0003 已接受口径)。
3. Phase B(2026-09-26 已落地——战斗舞台 Node2D 迁移批,台账 170-173)
- BattleStageView 同构改造已实施:PanelContainer → Control 壳(类名/252px/树内位置不变)+ Node2D 舞台半区(SubViewportContainer/SubViewport/StageWorld)+ Control 文本半区(round_label/stage_center/log_view 三件保留);站桩 StandeeView(Control)退役删除,本体 = StandeeBody(Node2D 世界精灵,行为面 17 方法契约保持);slot_row+悬停 tooltip 保留 Control 载体挂壳层(伴随件容器 = enemy_box 语义改写)。
- Phase A 交付的两挂点已被 Phase B 复用:set_world_font 字体显式注入(C6 判据=注入非空+has_char+宽度>0)与坐标空间分界纪律(世界=局部排布,壳层换算单点 world_to_shell)。
- 样张硬前置改判(用户 2026-09-26 裁决①):样张批冻结只卡换皮/演出批(循环 2 前置),不卡结构迁移——本节原「Phase B 开工硬前置 = 样张批冻结」条款据此作废;ADR-0006「只卡换皮不卡开工」原则的落文位。
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 各验)。