Files
spellforge/docs_dev/specs/2026-07-22-designer-ui-optimize-design.md
T

8.4 KiB
Raw Blame History

游戏设计器 UI 优化(布局撑满 + 元素复选框 + 双语标签)— 设计

  • 日期:2026-07-22
  • 状态:设计已确认,待实现
  • 范围:编辑器工具层 addons/game_designer/游戏运行时代码与数据文件格式零改动

1. 背景与问题

用户反馈游戏设计器三点待优化:

  1. 输入框无法完整显示内容 —— designer_ui.gdline()/opt()/spin() 只设固定最小宽度、无 SIZE_EXPAND_FILL,控件不随面板拉伸,长文本(显示名/描述/VFX/JSON/长中文选项)被截断。
  2. 法术元素标签用逗号文本易错 —— 应改可视化控件。元素标签是多值(如 plasma_storm = ["tag:water","tag:lightning"])。
  3. 部分显示仍为纯英文 —— spell_tab.TYPE_NAMES/LOGIC_OPSwave_tab.TYPE_NAMESresonance_tab.MATCH 等为纯英文。要求统一**双语(中文 English**显示,含 spell_tab(其 DMG_LABELS/ST_LABELS 现为纯中文,也改双语)。

词表事实

  • 元素标签词表(全数据仅这 5 个):tag:fire 火 / tag:ice 冰 / tag:lightning 雷 / tag:water 水 / tag:poison 毒
  • 枚举整数来源:DamageType0物理/1火/2冰/3雷/4毒/5奥术)、StatusID(0无/1燃烧/2冻结/3中毒/4潮湿/5沾油/6眩晕/7连击/8易伤)。

2. 布局修复(全 7 页,中央改动)

addons/game_designer/designer_ui.gdline() / opt() / spin() 三个工厂各追加:

ctrl.size_flags_horizontal = Control.SIZE_EXPAND_FILL

效果:行内 cell_label70px,autosize,不变)之后,输入控件吃掉剩余宽度;表格页(敌人/波次)各列随之填满面板宽度。零数据改动,全 7 页受益。


3. 共享元素词表(designer_ui.gd 新增常量)

const ELEM_LABELS := ["火 Fire", "冰 Ice", "雷 Lightning", "水 Water", "毒 Poison"]  # 显示(双语)
const ELEM_TAGS   := ["tag:fire", "tag:ice", "tag:lightning", "tag:water", "tag:poison"]  # 存储,index 对齐

spell_tab(复选框)与 resonance_tab(元素下拉)共用,单一事实源。通过 UI.ELEM_LABELS / UI.ELEM_TAGS 访问(const UI = preload(designer_ui.gd),常量可跨脚本访问)。


4. spell_tab —— 元素复选框 + 双语标签

4.1 元素标签复选框(替代 _tags 单行文本)

  • UI:一行「元素标签」标签 + 5 个 CheckBox(文本取 UI.ELEM_LABELS);下方一行「自定义标签」+ 小 LineEdit(收纳非元素 tag,防丢)。
  • 新增成员:var _elem_checks: Array = []5 个 CheckBox)、var _custom_tags: LineEdit
  • 加载(_on_select):
    var etags: Array = d.get("element_tags", [])
    for i in _elem_checks.size():
        _elem_checks[i].button_pressed = UI.ELEM_TAGS[i] in etags
    var custom := []
    for t in etags:
        if not (t in UI.ELEM_TAGS): custom.append(String(t))
    _custom_tags.text = ",".join(custom)
    
  • 应用(_on_apply,替换原 _tags 解析块):
    var tags: Array = []
    for i in _elem_checks.size():
        if _elem_checks[i].button_pressed: tags.append(UI.ELEM_TAGS[i])
    for t in _custom_tags.text.split(",", false):
        var s := t.strip_edges()
        if s != "" and not (s in tags): tags.append(s)
    
  • ⚠️ 多元素法术会被规整为固定元素顺序(火冰雷水毒),element_tags 语义为集合、顺序无意义——属预期简化(回读比较按集合)。

4.2 双语标签(仅显示;存储 index/key 不变)

const TYPE_NAMES := ["动作 ACTION", "修饰 MODIFIER", "触发 TRIGGER", "逻辑 LOGIC"]
const ACTION_KINDS := ["弹道 Projectile", "毒池 Zone", "召唤 Summon"]
const LOGIC_OPS := ["every_n_shots", "if_hp_below", "if_enemy_nearby", "loop"]  # 存储 key,不变
const LOGIC_OP_LABELS := ["每 N 次 every_n_shots", "生命低于 if_hp_below", "附近有敌 if_enemy_nearby", "循环 loop"]  # 新增,供下拉显示
const DMG_LABELS := ["物理 Physical", "火 Fire", "冰 Ice", "雷 Lightning", "毒 Poison", "奥术 Arcane"]
const ST_LABELS := ["无 None", "燃烧 Burn", "冻结 Freeze", "中毒 Poison", "潮湿 Wet", "沾油 Oily", "眩晕 Stun", "连击 Combo", "易伤 Vuln"]
  • _lop 下拉改用 UI.opt(LOGIC_OP_LABELS, 0)_read_metameta["logic_op"] = LOGIC_OPS[_lop.selected]_on_select_lop.selected = LOGIC_OPS.find(...)(index↔key 逻辑不变,只换显示数组)。
  • 列表前缀 "[%s] %s" % [TYPE_NAMES[...], sid] 变为 "[动作 ACTION] action_spark_bolt"(可接受)。

5. wave_tab —— 双语标签

const TYPE_NAMES = ["杂兵 Basic", "快速 Fast", "护甲 Armored", "精英 Elite", "首领 MiniBoss", "终焉 Boss"]
const BOSS_NAMES = ["无 None", "首领 MiniBoss", "终焉 Boss"]  # 存储走 BOSS_VALS,不变

(存储均为 index / BOSS_VALS,改标签零数据影响。)


6. resonance_tab —— 双语标签 + 元素下拉

  • 匹配方式:MATCH(存储 key adjacent/anywhere_in_deck)不变;新增 const MATCH_LABELS = ["相邻 adjacent", "卡组内任意 anywhere_in_deck"] 供下拉显示。_match = _row("匹配方式", UI.opt(MATCH_LABELS, 0))_on_apply"match": MATCH[_match.selected]_on_select_match.selected = max(0, MATCH.find(...))
  • 元素 A / B_p0/_p1 由 LineEdit → UI.opt(UI.ELEM_LABELS, 0)
    • 加载:_p0.selected = max(0, UI.ELEM_TAGS.find(String(p0)))(非元素值回退第 0 项)。
    • 应用:"pattern": [UI.ELEM_TAGS[_p0.selected], UI.ELEM_TAGS[_p1.selected]]
    • 列表显示 _refresh 用 pattern 原值(tag 串),不变。
    • ⚠️ 若某配方 pattern 含非元素值(现数据无),会被下拉回退丢失——现 resonance.json 全为元素,风险为零;如未来需要非元素 pattern 再扩展。

7. 已双语的页(enemy / core / status

enemy_tab.NAMES"杂兵 Basic")、core_tab.TOPO/FEAT"MATRIX 矩阵")、status_tab.STACK"REFRESH 刷新")已是双语,本次不改文案(其中 enemy name 是持久化字段,改动会写入 enemies.json),仅受益于第 2 点布局撑满。

注:core/status 现为「英文 中文」序,新加的为「中文 英文」序,序不统一但均双语、均含中文,本次不做归一化以免无谓改动持久化/工作正常的部分。


8. 组件边界与改动清单

文件 改动
designer_ui.gd line/opt/spin 加 SIZE_EXPAND_FILL;新增 ELEM_LABELS/ELEM_TAGS 常量
spell_tab.gd 元素复选框 + 自定义框替换 _tagsTYPE_NAMES/ACTION_KINDS/DMG_LABELS/ST_LABELS 改双语;新增 LOGIC_OP_LABELS 并用于 _lop 下拉
wave_tab.gd TYPE_NAMES/BOSS_NAMES 改双语
resonance_tab.gd MATCH_LABELS 新增用于下拉;元素 A/B 改元素下拉

其它运行时脚本、数据文件、其它 tab 逻辑不动。


9. 验证

  1. validate_script 四个改动文件全绿;reload_project + get_editor_errors 无报错。
  2. 回读往返测试(execute_editor_script):
    • spell:逐条 _on_select_read_meta meta 一致(同上一特性);element_tags 作为集合与原一致;含非元素自定义 tag 的情形自定义框保留。
    • resonance:逐条 _on_select→ 重建 recordpattern(两元素)与 match 与原一致。
  3. get_editor_screenshot 目测:各页输入框撑满面板宽、下拉显示双语、法术页元素为复选框。

10. 范围外(YAGNI

  • 不改游戏运行时代码 / 数据文件格式 / 数据内容。
  • 不归一化 enemy/core/status 既有双语文案。
  • 不做元素标签之外的 tag 下拉词表(自定义框已覆盖)。
  • resonance pattern 仍限两元素(现结构),不扩展多元素配方。

11. 验收标准

  1. 打开游戏设计器,各页输入框/下拉框随面板宽度撑满,长文本不再截断。
  2. 法术页「元素标签」为 5 个元素复选框 + 自定义框;plasma_storm 正确勾选 水+雷;保存后 element_tags 集合不变。
  3. 法术页 类型/动作种类/logic_op/伤害类型/施加状态 均双语显示,保存的 type/logic_op/damage_type 等值不变。
  4. 波次页 类型/Boss、共鸣页 匹配方式 均双语;共鸣页 元素 A/B 为元素下拉,resonance.json 回读往返一致。
  5. validate_script/reload_project 无报错;spell 与 resonance 回读往返测试 FAILS=0。