# 游戏设计器 UI 优化(布局撑满 + 元素复选框 + 双语标签)— 设计 - 日期:2026-07-22 - 状态:设计已确认,待实现 - 范围:编辑器工具层 `addons/game_designer/`,**游戏运行时代码与数据文件格式零改动** --- ## 1. 背景与问题 用户反馈游戏设计器三点待优化: 1. **输入框无法完整显示内容** —— `designer_ui.gd` 的 `line()`/`opt()`/`spin()` 只设固定最小宽度、无 `SIZE_EXPAND_FILL`,控件不随面板拉伸,长文本(显示名/描述/VFX/JSON/长中文选项)被截断。 2. **法术元素标签用逗号文本易错** —— 应改可视化控件。元素标签是**多值**(如 `plasma_storm` = `["tag:water","tag:lightning"]`)。 3. **部分显示仍为纯英文** —— `spell_tab.TYPE_NAMES`/`LOGIC_OPS`、`wave_tab.TYPE_NAMES`、`resonance_tab.MATCH` 等为纯英文。要求统一**双语(中文 English)**显示,含 spell_tab(其 `DMG_LABELS`/`ST_LABELS` 现为纯中文,也改双语)。 ### 词表事实 - 元素标签词表(全数据仅这 5 个):`tag:fire 火` / `tag:ice 冰` / `tag:lightning 雷` / `tag:water 水` / `tag:poison 毒`。 - 枚举整数来源:`DamageType`(0物理/1火/2冰/3雷/4毒/5奥术)、`StatusID`(0无/1燃烧/2冻结/3中毒/4潮湿/5沾油/6眩晕/7连击/8易伤)。 --- ## 2. 布局修复(全 7 页,中央改动) `addons/game_designer/designer_ui.gd`:`line()` / `opt()` / `spin()` 三个工厂各追加: ```gdscript ctrl.size_flags_horizontal = Control.SIZE_EXPAND_FILL ``` 效果:行内 `cell_label`(70px,autosize,不变)之后,输入控件吃掉剩余宽度;表格页(敌人/波次)各列随之填满面板宽度。零数据改动,全 7 页受益。 --- ## 3. 共享元素词表(`designer_ui.gd` 新增常量) ```gdscript 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`): ```gdscript 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` 解析块): ```gdscript 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 不变) ```gdscript 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_meta` 仍 `meta["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 —— 双语标签 ```gdscript 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. 既有双语标签归一为「中文 English」序 统一所有枚举下拉显示为「中文 English」序(存储均为 index,改文案零数据影响): - `core_tab.gd`(`addons/game_designer/core_tab.gd`): ```gdscript const TOPO = ["线性 LINEAR", "矩阵 MATRIX", "电路 CIRCUIT"] const FEAT = ["无 None", "记忆 PERSISTENT_MEMORY", "双流 DUAL_STREAM", "末位施法 ALWAYS_CAST_LAST", "洗牌 SHUFFLE_DECK", "无限法术 INFINITE_SPELLS"] ``` (`FEAT` 原有 3 项纯英文 `ALWAYS_CAST_LAST`/`SHUFFLE_DECK`/`INFINITE_SPELLS` 补中文;`topology`/`feature_tags` 存 index 不变。) - `status_tab.gd`(`addons/game_designer/status_tab.gd`): ```gdscript const STACK = ["刷新 REFRESH", "叠层 INTENSITY", "独立 INDEPENDENT"] ``` (`stack_mode` 存 index 不变。) **不改**: - `enemy_tab.NAMES`(`["杂兵 Basic", ...]`)已是「中文 English」序,且 enemy `name` 是**持久化字段**(写入 enemies.json)——保持原样。 - `balance_tab` 标签本就中文(`敌人血量×`/`初学者`/`MiniBoss(W8) 血量` 等),无纯英文枚举下拉,无需改。 全 7 页均受益于第 2 点布局撑满。 --- ## 8. 组件边界与改动清单 | 文件 | 改动 | |---|---| | `designer_ui.gd` | line/opt/spin 加 `SIZE_EXPAND_FILL`;新增 `ELEM_LABELS`/`ELEM_TAGS` 常量 | | `spell_tab.gd` | 元素复选框 + 自定义框替换 `_tags`;`TYPE_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 改元素下拉 | | `core_tab.gd` | `TOPO`/`FEAT` 归一为「中文 English」序(FEAT 补 3 项中文) | | `status_tab.gd` | `STACK` 归一为「中文 English」序 | 其它运行时脚本、数据文件、其它 tab 逻辑不动。`enemy_tab`/`balance_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`→ 重建 record,`pattern`(两元素)与 `match` 与原一致。 3. `get_editor_screenshot` 目测:各页输入框撑满面板宽、下拉显示双语、法术页元素为复选框。 ## 10. 范围外(YAGNI) - 不改游戏运行时代码 / 数据文件格式 / 数据内容。 - 不动 `enemy_tab.NAMES`(持久化)与 `balance_tab` 中文标签。 - 不做元素标签之外的 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。