8.0 KiB
法术编辑器并入 game_designer + 字段化表单 — 设计
- 日期:2026-07-21
- 状态:设计已确认,待实现
- 范围:编辑器工具层(
addons/)+ 文档,游戏运行时代码零改动
1. 背景与问题
当前有两个已启用的编辑器插件:
addons/spell_editor/(独立插件,左下停靠面板,入口spell_editor.gd→spell_dock.gd)addons/game_designer/(底部面板,designer_panel.gd用TabContainer整合多个 tab)
designer_panel.gd 已经把 spell_dock.gd 复用为「✨ 法术」标签页 → 法术编辑器同时出现在两处,入口冗余。
同时,spell_dock.gd 的 meta 字段是一个自由格式 JSON TextEdit,随法术类型/子类型变化很大,容易配置错误、可读性差 —— 与 core_tab.gd(字段化输入框)风格不一致。
目标
- 并入:法术编辑只保留在
game_designer底部面板的 tab 里;删除独立spell_editor插件。 - 字段化:用按类型动态切换的输入框表单替代原始 JSON
meta,对齐core_tab.gd风格。
2. 结构变更(并入)
- 新建
addons/game_designer/spell_tab.gd,结构对齐core_tab.gd:@tool extends VBoxContainerconst UI = preload("res://addons/game_designer/designer_ui.gd")- 复用
UI.spin / line / opt / btn / cell_label / header / status_label / set_status / load_json / save_json - 布局:标题 → 列表(ItemList) →
➕新增 / ⧉复制 / 🗑删除→ 表单区 →✓应用到列表 / 💾保存文件 / ↻重载→ 状态行
designer_panel.gd第 12 行:- 由
preload("res://addons/spell_editor/spell_dock.gd").new() - 改为
preload("res://addons/game_designer/spell_tab.gd").new()
- 由
- 删除独立插件:
- 从
project.godot[editor_plugins] enabled数组移除"res://addons/spell_editor/plugin.cfg" - 删除整个
addons/spell_editor/目录(spell_editor.gd(.uid)、spell_dock.gd(.uid)、plugin.cfg)
- 从
- 数据路径与文件格式不变:
res://data/spells.json;SpellRegistry运行时加载逻辑零改动。
3. 动态表单(核心)
3.1 固定区(所有类型)
ID(LineEdit,唯一) / 类型(OptionButton: ACTION/MODIFIER/TRIGGER/LOGIC) / 显示名(LineEdit) / 描述(LineEdit) / 元素标签(LineEdit,逗号分隔)。
3.2 通用 meta 区(所有类型常驻)
mana_cost(int) / shop_cost(int) / unlock_cost(int)。
3.3 动态 meta 区
一个占位容器(如 VBoxContainer),随 类型 变化(ACTION 额外看 action_kind 子选择、LOGIC 额外看 logic_op 子选择)销毁并重建其子节点。字段 schema:
| 类型 | 子选择 | 字段(控件类型) |
|---|---|---|
| ACTION | 弹道(默认,action_kind 省略) | base_damage(float) speed(float) lifetime(float) radius(float) damage_type(enum) apply_status_id(enum,含「无」) apply_combo_mark(bool) pierce(int) |
| ACTION | zone 毒池(action_kind="zone") | radius(float) status_id(enum) duration(float) tick_interval(float) |
| ACTION | summon 召唤(action_kind="summon") | lifetime(float) range(float) fire_interval(float) base_damage(float) speed(float) damage_type(enum) |
| MODIFIER | — | damage_add(float) damage_mult(float) spread_add(int) multicast(int) pierce(int) heavy_cost(int) mana_leech(int) |
| TRIGGER | — | base_damage(float) speed(float) lifetime(float) radius(float) damage_type(enum) |
| LOGIC | every_n_shots | n(int) reg(int) |
| LOGIC | if_hp_below | threshold(float, 0–1) |
| LOGIC | if_enemy_nearby | range(float) |
| LOGIC | loop | count(int) |
- ACTION 的
action_kind用一个 OptionButton 子选择器:弹道 / zone 毒池 / summon 召唤;选「弹道」时不写action_kind键。 - LOGIC 的
logic_op用一个 OptionButton 子选择器:every_n_shots / if_hp_below / if_enemy_nearby / loop;始终写入logic_op字符串。
3.4 枚举下拉(整数存储)
标签来自域枚举,控件用 UI.opt(...),存整数值:
damage_type(scripts/autoloads/damage_type.gd):0 物理 / 1 火 / 2 冰 / 3 雷 / 4 毒 / 5 奥术status(apply_status_id/status_id,scripts/autoloads/status_id.gd):0 无 / 1 燃烧 / 2 冻结 / 3 中毒 / 4 潮湿 / 5 沾油 / 6 眩晕 / 7 连击 / 8 易伤- 下拉 index 即整数值(0 = 无 = 不写该键)。
枚举标签在
spell_tab.gd内以常量数组硬编码,index 对齐上述整数值;若域枚举新增值,需同步更新此数组(在文件顶部注释标注来源)。
4. 数据安全(防丢键 / 保持简洁)
4.1 「其它(JSON)」逃生舱
动态区下方放一个小 TextEdit(标签「其它(JSON)」)。
- 加载(选中法术):把
meta中 schema 已知键填入对应控件;剩余未知键打包成 JSON 字符串填入此框(无剩余则空)。 - 应用:最终
meta= 通用字段 + 当前类型/子类型动态字段 + 解析「其它(JSON)」得到的键(后者仅补充未被 schema 覆盖的键;若解析失败则报错、拒绝应用)。
作用:游戏日后新增 meta 键、或某法术带非常规键时,编辑器不会静默丢弃。
4.2 可选键清洁写出
以下键在值为默认(0 / false / "" / 未设)时不写入 JSON,保持与现有 spells.json 一致的简洁:
unlock_cost(=0) / apply_status_id(=0) / apply_combo_mark(=false) / pierce(=0) / damage_mult(=1.0,乘数默认值,等于 1.0 时不写) / spread_add(=0) / multicast(=0) / heavy_cost(=0) / mana_leech(=0) / reg(=0)。
mana_cost/shop_cost始终写入(含 0,等离子风暴mana_cost:0 shop_cost:0是有意义的显式 0)。
4.3 回读验证目标
现有 data/spells.json 全部 22 条必须能被正确回读进新表单(含 zone、summon、全部 4 种 logic_op),再次「应用+保存」后语义等价(允许可选默认键因 §4.2 被省略,属预期简化)。
5. 组件边界
| 单元 | 职责 | 依赖 |
|---|---|---|
spell_tab.gd |
法术 tab 全部 UI + 增删改查 + schema 驱动的表单构建/读写 | designer_ui.gd;data/spells.json |
designer_panel.gd |
只改一行 preload 指向新 tab | spell_tab.gd |
project.godot |
移除 spell_editor 插件启用项 | — |
spell_tab.gd 内部建议拆出:_schema_for(type, sub) 返回字段描述数组;_rebuild_dynamic() 依 schema 建控件;_read_form()/_write_form(meta) 在控件与 meta 字典间搬运。schema 表是唯一事实源,加/改字段只动这张表。
6. 文档
更新 docs/handbook/06_spell_editor.md:
- 入口改为「Godot 编辑器底部面板 → 🎮 游戏设计器 → ✨ 法术 标签页」。
- 删除「1. 启用插件(左下停靠区)」相关描述。
- 界面示意图改为字段化表单(固定区 + 通用区 + 动态区 + 其它JSON)。
- 「meta 字段速查」保留(作为字段语义参考),补一句「现由表单按类型呈现」。
7. 范围外(YAGNI)
- 不动其它 tab(敌人/波次/Core/共鸣/状态/平衡)。
- 不动游戏运行时代码(
SpellRegistry/ VM /wand_preset.gd)。 - 不做字段级校验规则引擎(超范围);仅做 ID 非空、JSON 合法性、枚举 index 合法性等基本校验。
- 不做 tag 的下拉选择器(元素标签仍为逗号分隔文本,沿用现状)。
8. 验收标准
- Godot 编辑器仅在底部「🎮 游戏设计器 → ✨ 法术」出现法术编辑,左下不再有独立「法术编辑器」停靠面板。
addons/spell_editor/目录已删除,project.godot不再引用它,编辑器加载无报错。- 逐一选中 22 条现有法术,字段正确回读(类型/子类型/枚举/数值/其它JSON 均对)。
- 新增一条各类型法术(含 zone、summon、每种 logic_op),应用+保存后
spells.json合法且游戏 F5 能加载。 - 改 ID 时旧键被移除;复制/删除正常。
- 「其它(JSON)」非法时应用被拒绝并给出错误状态,不写坏数据。