Files
spellforge/docs_dev/specs/2026-07-21-spell-editor-fieldize-design.md
T

8.0 KiB
Raw Blame History

法术编辑器并入 game_designer + 字段化表单 — 设计

  • 日期:2026-07-21
  • 状态:设计已确认,待实现
  • 范围:编辑器工具层(addons/+ 文档,游戏运行时代码零改动

1. 背景与问题

当前有两个已启用的编辑器插件:

  • addons/spell_editor/(独立插件,左下停靠面板,入口 spell_editor.gdspell_dock.gd
  • addons/game_designer/(底部面板,designer_panel.gdTabContainer 整合多个 tab

designer_panel.gd 已经把 spell_dock.gd 复用为「 法术」标签页 → 法术编辑器同时出现在两处,入口冗余

同时,spell_dock.gdmeta 字段是一个自由格式 JSON TextEdit,随法术类型/子类型变化很大,容易配置错误、可读性差 —— 与 core_tab.gd(字段化输入框)风格不一致。

目标

  1. 并入:法术编辑只保留在 game_designer 底部面板的 tab 里;删除独立 spell_editor 插件。
  2. 字段化:用按类型动态切换的输入框表单替代原始 JSON meta,对齐 core_tab.gd 风格。

2. 结构变更(并入)

  • 新建 addons/game_designer/spell_tab.gd,结构对齐 core_tab.gd
    • @tool extends VBoxContainer
    • const 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.jsonSpellRegistry 运行时加载逻辑零改动。

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, 01)
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_typescripts/autoloads/damage_type.gd):0 物理 / 1 火 / 2 冰 / 3 雷 / 4 毒 / 5 奥术
  • statusapply_status_id / status_idscripts/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.gddata/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. 验收标准

  1. Godot 编辑器仅在底部「🎮 游戏设计器 → 法术」出现法术编辑,左下不再有独立「法术编辑器」停靠面板。
  2. addons/spell_editor/ 目录已删除,project.godot 不再引用它,编辑器加载无报错。
  3. 逐一选中 22 条现有法术,字段正确回读(类型/子类型/枚举/数值/其它JSON 均对)。
  4. 新增一条各类型法术(含 zone、summon、每种 logic_op),应用+保存后 spells.json 合法且游戏 F5 能加载。
  5. 改 ID 时旧键被移除;复制/删除正常。
  6. 「其它(JSON)」非法时应用被拒绝并给出错误状态,不写坏数据。