Files
pixelheros/assets/script/game/common/config/英雄配置UI设计方案.md
panFD 1739e2324c feat: 调整技能配置与英雄技能组,修复技能分类约束
1.  重构技能分类,将6013~6019标记为仅可作大招
2.  修改紫电魔女普攻为6010紫魔球,添加麻痹效果
3.  更新技能prefab参数与技能配置表
4.  调整英雄UI配置过滤规则
2026-08-20 13:31:00 +08:00

243 lines
16 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.
# 英雄配置 UI 设计方案
> 为编辑器扩展 `extensions/hero_setup` 新增「英雄配置」功能的设计文档。
> 目标:以 [SkillSet.ts](./SkillSet.ts) / [BuffSet.ts](./BuffSet.ts) / FieldSkillSet 为只读底座,通过 UI 配置英雄(新增 + 编辑现有),落地 [英雄设计方案.md](./英雄设计方案.md) 与 [英雄设计指引.md](./英雄设计指引.md) 的能力池范式。
>
> **已确认决策**
> - lv 档位按文档完整支持 **lv2/3/5/7/9**(数据先行),超过运行时 `HERO_MAX_LV=6` 的档给黄色警告不阻止
> - 落地方式 **JSON 外部化**JSON 为 source of truth单向生成 heroSet.ts 数据段
> - 功能范围:**新增英雄 + 编辑现有英雄**(不做删除)
> - 校验uuid 存在性 + 段位规则 + lv 升序 + t_num≥1 + 形态硬约束
> - 生成器边界:**标记注释定界**(分歧裁定 1
> - 怪物段:**全量迁移进 JSON、UI 只读**(分歧裁定 2round-trip 门保真)
---
## 一、总体架构与数据流
```
heroSet.data.jsonsource of truthgit 跟踪)
│ ① 扩展保存时生成(单向 JSON→TS
heroSet.ts 数据段HeroInfo 表 + HeroList = 生成产物,其余 ~500 行类型/函数原样保留)
│ ② 下游零改动
CardPoolList 自动派生 eval-heroes 强度评估 游戏运行时
```
```
面板Vue3同面板双 tab强度评估英雄配置
│ Editor.Message.request('hero_setup', ...)
主进程 methods ──spawn──► scripts/heroes-cli.ts复用 tsx + tsconfig.eval.json + mock-cc 链路)
│ --cmd=query-base | export | validate
├─ 读 JSON / 校验
└─ save备份 → 写 TS → 语法自检 → round-trip 比对 → 写 JSON → refresh-asset
```
关键原则:
- **运行时零改动**游戏代码、CardSet 派生链、eval-heroes、下游 49 个 import 方均不感知本功能存在
- **同步方向单向**JSON → heroSet.tsTS 数据段永远为生成产物;不一致时弹冲突对话框(含 diff提供一次性「以 TS 反向导入」通道,禁止静默覆盖
- **CardPoolList 无需手动同步**:新英雄写入 HeroInfo + HeroList 后CardSet 模块加载期自动生成卡池条目
## 二、JSON 存储
### 2.1 位置
`assets/script/game/common/config/heroSet.data.json`
| 候选 | 结论 | 理由 |
|---|---|---|
| assets/script/.../heroSet.data.json | **采用** | 与 heroSet.ts 同目录、随 git 入库 |
| assets/resources/ 下 | 否决 | resources 目录全量打包进游戏 |
| extensions/hero_setup/ 内 | 否决 | `.gitignore` 整体忽略 extensions/*source of truth 会丢版本历史 |
| 项目根 config/ | 备选 | 零 asset-db 干扰,但离代码远、易被误删 |
JSON 不被任何运行时代码 importCocos 构建不会打包它asset-db 会为其生成 .meta代价可接受。
### 2.2 Schema 要点
```typescript
interface HeroConfigFile {
schema_version: 1; // 结构性变更时递增,读取端按版本迁移
generated_at: string; // ISO 时间(人读)
gen_hash: string; // 上次生成 TS 数据段指纹,用于漂移检测
heroes: Record<number, HeroJson>; // 英雄5001~5499 段)
monsters: Record<number, HeroJson>; // 怪物6001~6199 段UI 只读)
}
```
`heroInfo` 接口逐字段对齐,仅三处转换:
| 转换 | JSON 存储 | 生成还原 |
|---|---|---|
| 枚举 | 语义字符串 `"type": "Melee"` | `HType.Melee`(生成端持名称→成员映射表) |
| 基准属性(英雄) | `{ ref: "HERO_ROLE_BASE", role: "Tank", delta: 1200 }` | `HERO_ROLE_BASE[HRole.Tank].hp + 1200`delta 0 省略 `+ 0` |
| 基准属性(怪物) | 绝对值 `hp: 3000` | 直接数值 |
| 攻速 cd | `atk_speed: "Slow"` | `AtkSpeedSet[AtkSpeedLv.Slow].cd` |
设计理由:
- 枚举存字符串diff 友好、手改安全、枚举成员重排不破坏数据
- 属性存「基准引用+增量」:保住 `HERO_ROLE_BASE` 模板调参全局传播语义heroSet.ts L145 注释意图);**禁止 JSON 出现已求值裸数值**,杜绝基准值改动后历史英雄静默漂移
- UI 端用底座数据实时计算绝对值预览(基准+增量双输入)
## 三、heroSet.ts 生成器(核心)
### 3.1 边界定位:标记注释定界
- 首次迁移(`--cmd=export`)时在 `export const HeroInfo` 前插入 `// @hero-setup:region-begin`,在 `HeroList` 数组 `];` 后插入 `// @hero-setup:region-end`
- 此后每次生成:读全文 → 定位两标记 → `前缀 + 生成段 + 后缀` 拼接
- **双保险**
- 标记被手删 → 拒绝生成并提示走「重新导入」流程,绝不退化为全文件重写
- 保存前重算当前 TS 数据段指纹与 JSON 的 `gen_hash` 比对,不一致 → 弹冲突对话框(展示 diff二选一以 TS 反向导入一次 / 以 JSON 覆盖)
### 3.2 自研序列化器(非 JSON.stringify
格式保真清单与现文件逐项对齐round-trip 门验证):
| 不变量 | 依赖方 |
|---|---|
| 4 空格缩进、双引号、行尾无分号风格跟随原文件 | 全部 diff 工具 |
| 条目头字段单行紧凑(`uuid, name, path, fac, card_lv, lv, type, grow_type, role, hp, ap, def` 一行),触发块换行展开 | 现有阅读习惯 |
| 按 uuid 段位插入注释块(`// ===== card_lv=55401-5499=====` | 段位规则 |
| **档位数组 lv 严格升序**lv_entries / LvTriggerEntry[] / field / revive / ap_bonus / hp_bonus | `HeroSkillDesc.ts:384``filter(e=>e.lv<=lv).pop()` |
| 数字键无引号、保持插入序 | Record 索引 |
| `ccd: 0` 占位字段不丢HSkillInfo 必填) | Hero.ts 加载 |
| 字段顺序稳定skills → atked/atking → dead → fstart → field → revive → info | 减少 diff 噪音 |
| HeroList 按 card_lv 分组升序 | CardSet 卡池派生顺序 |
| `info: ""` 收尾、`path/icon/info` 字符串原样不转义变换 | 文本保真 |
| **文件头 BOM 原样保留**(读 utf-8 后首字符检测写回) | 避免整文件 diff |
### 3.3 Round-trip 门
每次保存后执行「重新生成 → 与落盘 TS 逐字节比对」,不等则回滚并报错。初始迁移同样以此门作为验收标准。
## 四、扩展模块与消息协议
### 4.1 新增文件
| 文件 | 职责 |
|---|---|
| `scripts/heroes-cli.ts` | Node 子进程脚本,承载 `--cmd=query-base / export / validate` 三命令;复用 `tsconfig.eval.json`paths: cc → mock-cc.ts直接 import SkillSet/BuffList/FieldSkillSet/heroSet |
| `source/heroes/generator.ts`(主进程侧) | JSON→TS 序列化器 + round-trip 校验(可被 heroes-cli 复用) |
| `source/panels/default/components/HeroConfig.ts` + `static/template/vue/hero-config.html` | 配置 UI 组件 |
### 4.2 消息协议
| 消息 | 方向 | 入参 | 出参 |
|---|---|---|---|
| `query-base` | 面板→主进程 | | 底座快照SkillSet/BuffList/FieldSkillSet 元数据 + 枚举名表;按文件 mtime 缓存) |
| `read-config` | 面板→主进程 | | `{ file: HeroConfigFile, drift?: boolean }`JSON 不存在则先跑 `--cmd=export` |
| `validate` | 面板→主进程 | HeroConfigFile | 校验报告error[] / warning[] |
| `save-config` | 面板→主进程 | HeroConfigFile | `{ ok, report }` |
| `config-saved` | 主进程→面板广播 | | 强度评估 tab 重算,形成「改→看」闭环 |
`package.json``contributions.messages` 补齐上述映射。
### 4.3 保存流水线(顺序固定)
```
校验通过 → 备份当前 heroSet.ts → 生成 TS 文本 → ts 语法自检createSourceFile 取 syntactic diagnostics
→ 写 TS临时文件 + rename 原子写)→ round-trip 逐字节比对 → 通过才写 JSON → asset-db refresh-asset
```
任一步失败:从备份恢复 TS、JSON 保持未写状态、返回错误报告。
## 五、校验规则分级
### 5.1 Error阻止保存
| 规则 | 说明 |
|---|---|
| uuid 段位匹配 | 新增时5001-5099=card1 / 5101-5199=card2 / 5201-5299=card3 / 5301-5399=card4 / 5401-5499=card5且不与现有重复编辑现有英雄 uuid 不可改) |
| skills/触发组 s_uuid 存在 | 必须在 SkillSet否则运行时静默不出手 |
| timed_buff_id 存在 | 必须在 BuffList 7001-7024 段 |
| field uuid 存在 | 必须在 FieldSkillSet 7001-7020 段(与 BuffList 同号段但互不相通) |
| t_num ≥ 1 | 0 会导致 `% 0 = NaN` 永不触发 |
| 档位 lv 升序不重复 | 保存时序列化器强制排序,重复档报错 |
| 形态硬约束 | tank/warrior→近战组普攻 6004、大招 6103~6106/6108assassin/射手→中距组6001~6003/6006~6008support/mage→远程组6009~6019、大招 6101/6102/6107/6109~6111辅助技能 6301/6302/6412/6501 不限形态 |
| 必填缺失 | name / path / type / role |
### 5.2 Warning确认后可存
| 规则 | 说明 |
|---|---|
| lv > 6 档位 | `HERO_MAX_LV=6` 封顶lv7/9 档当前永不生效(数据先行,运行时扩展上限后自动生效) |
| card_lv 1/2/3 配了 field | 应改走 ap_bonus/hp_bonus30/40/50% 补偿) |
| card_lv 4/5 的 lv9 未配 field | 按设计指引应有驻场光环 |
| infos 条数与实际档位不匹配 | 面板逐条展示依赖 |
## 六、UI 设计
### 6.1 布局
同面板default1024×600 dockable顶部 `ui-tab`:「强度评估|英雄配置」。三栏:
```
┌ [强度评估 | 英雄配置●] ────────────────────────── ●=有未保存改动 ┐
├────────────┬──────────────────────────────┬─────────┤
│ 🔍搜索 │ 锚点导航: 基础|技能|触发|光环|复活|加成|说明 │ 校验抽屉 │
│ ▾LV5 │ ①基础: uuid/name/path/card_lv/role/type │ ✕2 ⚠1 │
│ ●5401 刀刀│ hp/ap/def 基准+增量双输入(显示合计) │ 点击 │
│ ▾LV4 … │ ②技能: [普攻|大招] SkillPicker + 底座只读条 │ 定位 │
│ [+新建] │ + OverridesEditor + 档位矩阵 │ [保存] │
└────────────┴──────────────────────────────┴─────────┘
```
### 6.2 组件清单
| 组件 | 职责 | 数据源 | 校验 |
|---|---|---|---|
| HeroListView | card_lv 分组、搜索、dirty 圆点、新建入口 | heroSet.data.json | |
| BaseForm | 基础字段hp/ap/def 基准+增量双输入 | HERO_ROLE_BASE | uuid 段位/重复role→type 联动锁定 |
| SkillPicker | 自制搜索下拉选 SkillSet显示 `6101 火球 · 远程 · AOE` | SkillSet | 普攻限 6001~6012 按 IType 过滤6013~6019 仅作大招);大招按形态过滤;辅助技能放行 |
| SkillBasePanel | 底座关键参数只读展示ap/cd/hit/IType/RType/DTType | SkillSet | |
| OverridesEditor | 分组编辑 overrides数值 ap/hit_count/num概率 crt/frz/stun击退 bckbuff 注入 timed_buff_id→联动 buff_value/buff_duration永久 buff_type召唤 call_hero | BuffList | timed_buff_id 存在性 |
| LvMatrix | lv 档位矩阵(技能 lv_entries / 触发档 / field / revive 复用变体):行=lv1/2/3/5/7/9、列=参数,可整行复制加档;**lv7/9 行黄色边框+⚠「运行时 HERO_MAX_LV=6 暂不生效」** | 当前英雄 | lv 升序不重复t_num≥1 |
| TriggerGroupEditor | s_uuid 卡片嵌套档位行(外层技能组、内层档位),保存时统一升序 | SkillSet | s_uuid 存在性 |
| InfosEditor | 9 档分级说明文本 | 当前英雄 | 条数与档位对照提示 |
| BonusEditor | ap_bonus/hp_bonus 档位lv+value | 当前英雄 | 仅 card_lv1/2/3 提示使用 |
| ValidationPanel | 错误/警告列表(区域·字段·消息),点击 scrollIntoView + 字段红框高亮;保存按钮 error>0 时禁用 | 校验器输出 | |
### 6.3 关键交互
- **ui- 组件与 Vue 混用**`isCustomElement` 已配置ui- 元素 `v-model` 不生效,须 `:value` + `@change`/`@confirm` 手动双向;属性用 kebab-case
- **自制组件**ui- 缺失):档位矩阵表格、可搜索 SkillPicker、BuffList 分组下拉、校验列表、确认 modal
- **新建向导**:单屏 modal——选 card_lv → role →(形态按映射自动锁定为只读徽章)→ 模板预填uuid 取段位内最小空位、职业基准属性(增量 0、按指引 2.6 能力组合范式推荐技能tank→6004+6301、support→6009+6302 等、lv3/5/7 空档骨架;提供「空白模板」选项
- **状态管理**dirty 深度 watch加载时存 snapshot 对比),列表项与 tab 标题打点;切换英雄/切 tab 弹确认(放弃/留下);底座加载沿用 spawn 异步 + loading 态
- **保存流程**error>0 → 保存禁用tooltip 显示错误数);仅 warning → 自制 modal 确认后执行保存流水线
## 七、失败恢复与安全
| 项 | 方案 |
|---|---|
| 备份 | `local/hero_setup/backup/heroSet.<HHmmss>.ts`local/ 已被 git 忽略),滚动保留 5 份;不用 git stash不污染用户暂存区 |
| 原子写 | 同目录临时文件 + renameWindows EPERM编辑器占用时 200ms 退避重试 3 次 |
| 语法自检 | `ts.createSourceFile` 取 syntactic diagnosticstypescript 已在依赖);语义级检查提示用户跑 `npx tsc --noEmit`,不阻塞 |
| 回滚 | 自检/round-trip 失败 → 从备份恢复原文件 → 返回错误报告JSON 保持未写 |
| asset-db 刷新 | 写后 `Editor.Message.request('asset-db', 'refresh-asset', 'db://assets/script/game/common/config/heroSet.ts')`;禁止「删文件+建新文件」式写法(触发 meta 重建) |
| 并发 | 面板内保存互斥in-flight promise 复用)+ 串行队列 |
## 八、初始迁移(一次性)
1. `--cmd=export`tsx import 现 heroSet.ts → 导出 `heroSet.data.json`26 英雄 + 14 怪物全量,怪物标记只读)
2. 从 JSON 重新生成 TS → **round-trip 逐字节比对必须通过**(迁移门)
3. 迁移门失败的处理:修序列化器直至通过(一次性成本);确实无法保真的字段(如怪物特有表达式)再评估局部文本透传回退
4. 迁移完成后 heroSet.ts 数据段即为生成产物,此后手改走「以 TS 反向导入」通道
## 九、实施阶段
| 阶段 | 内容 | 验收 |
|---|---|---|
| 1. 脚本核心 | heroes-cli.ts + JSON schema + 导入器 + 序列化生成器 + round-trip 门(纯 Node 可单测) | 迁移门逐字节通过 |
| 2. 主进程 | methods + 消息协议 + 备份/原子写/自检/刷新 | 手工消息调用保存成功、编辑器感知刷新 |
| 3. 面板骨架 | tab + 英雄列表 + 基础表单 + SkillPicker/OverridesEditor | 可加载、可编辑基础字段 |
| 4. 完整表单 | 触发组/光环/复活/infos/bonus + 校验抽屉 + 新建向导 | 校验规则全覆盖、按设计指引录入新英雄走通 |
| 5. 联调冒烟 | eval-heroes 验证加载链、形态约束用例、按指引落地 1 个 P0 英雄(紫电魔女 5301 | 强度评估 tab 正常评分 |
## 十、遗留事项
- heroSet.ts:297 注释「怪物5200段」与实际 6001/6101 段不符(幽灵注释),迁移时顺带修正
- UI 支持 lv7/9 档而 `HERO_MAX_LV=6`HeroSkillDesc.ts:321-331 会向玩家展示永远无法解锁的 Lv7/Lv9 文案——超 6 档仅预录入,运行时/文案侧需按 HERO_MAX_LV 裁剪(独立任务,不在本功能范围)
- 未来扩展 `HERO_MAX_LV` 至 9 时,需同步升级碎片表 `HERO_UPGRADE_NEED_FRAGMENTS`GameSet.ts:70