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

166 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 游戏设计器 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。