Files
pixelheros/assets/script/game/common/config/英雄配置UI设计方案.md
pan d9bccd7c93 refactor: 将代码中的card_lv统一替换为rarity
本次提交完成了全项目范围内术语的统一替换,将原本指代卡牌品质/等级的card_lv字段全部重命名为rarity,对齐了设计文档中的命名规范,避免了术语混淆:
1.  更新了所有配置文件、组件类、数据结构中的字段定义
2.  同步修改了注释、文档说明中的相关表述
3.  修正了英雄、技能、装备卡牌的品质显示逻辑
4.  调整了背景颜色切换、属性计算等相关业务逻辑
2026-09-02 16:42:54 +08:00

16 KiB
Raw Blame History

英雄配置 UI 设计方案

为编辑器扩展 extensions/hero_setup 新增「英雄配置」功能的设计文档。 目标:以 SkillSet.ts / BuffSet.ts / FieldSkillSet 为只读底座,通过 UI 配置英雄(新增 + 编辑现有),落地 英雄设计方案.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 只读(分歧裁定 2,round-trip 门保真)

一、总体架构与数据流

heroSet.data.json(source of truth,git 跟踪)
        │ ① 扩展保存时生成(单向 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.ts,TS 数据段永远为生成产物;不一致时弹冲突对话框(含 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 不被任何运行时代码 import,Cocos 构建不会打包它;asset-db 会为其生成 .meta,代价可接受。

2.2 Schema 要点

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, rarity, lv, type, grow_type, role, hp, ap, def 一行),触发块换行展开 现有阅读习惯
按 uuid 段位插入注释块(// ===== rarity=5(5401-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 按 rarity 分组升序 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、大招 61006104);assassin/射手→中距组(60016003/60066008,大招 61506151);support/mage→远程组(60096012、大招 62006204);辅助技能 6301/6302/6412/6501 不限形态
必填缺失 name / path / type / role

5.2 Warning(确认后可存)

规则 说明
lv > 6 档位 HERO_MAX_LV=6 封顶,lv7/9 档当前永不生效(数据先行,运行时扩展上限后自动生效)
rarity 1/2/3 配了 field 应改走 ap_bonus/hp_bonus(30/40/50% 补偿)
rarity 4/5 的 lv9 未配 field 按设计指引应有驻场光环
infos 条数与实际档位不匹配 面板逐条展示依赖

六、UI 设计

6.1 布局

同面板(default,1024×600 dockable)顶部 ui-tab:「强度评估|英雄配置」。三栏:

┌ [强度评估 | 英雄配置●] ────────────────────────── ●=有未保存改动 ┐
├────────────┬──────────────────────────────┬─────────┤
│ 🔍搜索     │ 锚点导航: 基础|技能|触发|光环|复活|加成|说明 │ 校验抽屉 │
│ ▾LV5       │ ①基础: uuid/name/path/rarity/role/type     │  ✕2 ⚠1 │
│  ●5401 刀刀│   hp/ap/def 基准+增量双输入(显示合计)      │  点击   │
│ ▾LV4 …     │ ②技能: [普攻|大招] SkillPicker + 底座只读条  │  定位   │
│ [+新建]   │   + OverridesEditor + 档位矩阵               │ [保存]  │
└────────────┴──────────────────────────────┴─────────┘

6.2 组件清单

组件 职责 数据源 校验
HeroListView rarity 分组、搜索、dirty 圆点、新建入口 heroSet.data.json –
BaseForm 基础字段;hp/ap/def 基准+增量双输入 HERO_ROLE_BASE uuid 段位/重复;role→type 联动锁定
SkillPicker 自制搜索下拉选 SkillSet,显示 6200 火焰喷射 · 远程 · AOE SkillSet 普攻限 6001~6012 按 IType 过滤;大招按形态过滤;辅助技能放行
SkillBasePanel 底座关键参数只读展示(ap/cd/hit/IType/RType/DTType) SkillSet –
OverridesEditor 分组编辑 overrides(数值 ap/hit_count/num|概率 crt/frz/stun|击退 bck|buff 注入 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) 当前英雄 仅 rarity1/2/3 提示使用
ValidationPanel 错误/警告列表(区域·字段·消息),点击 scrollIntoView + 字段红框高亮;保存按钮 error>0 时禁用 校验器输出 –

6.3 关键交互

  • ui- 组件与 Vue 混用:isCustomElement 已配置;ui- 元素 v-model 不生效,须 :value + @change/@confirm 手动双向;属性用 kebab-case
  • 自制组件(ui- 缺失):档位矩阵表格、可搜索 SkillPicker、BuffList 分组下拉、校验列表、确认 modal
  • 新建向导:单屏 modal——选 rarity → 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(不污染用户暂存区)
原子写 同目录临时文件 + rename;Windows EPERM(编辑器占用)时 200ms 退避重试 3 次
语法自检 ts.createSourceFile 取 syntactic diagnostics(typescript 已在依赖);语义级检查提示用户跑 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)