引导配置可视化
This commit is contained in:
parent
d6bc6f1fb6
commit
8701c43bc5
@ -71,6 +71,16 @@ public class GuideConfigEditor : EditorWindow
|
||||
private enum SortMode { Id, Priority, Name }
|
||||
private SortMode _sortMode = SortMode.Id;
|
||||
|
||||
// 预设值缓存(从现有数据中收集)
|
||||
private List<string> _satisfyPresets = new List<string>();
|
||||
private List<string> _triggerPresets = new List<string>();
|
||||
private List<string> _endPresets = new List<string>();
|
||||
private List<string> _roleExpressionPresets = new List<string>();
|
||||
private List<string> _dialogPosPresets = new List<string>();
|
||||
private List<string> _clickPosPresets = new List<string>();
|
||||
private List<string> _focusPosPresets = new List<string>();
|
||||
private bool _presetsDirty = true;
|
||||
|
||||
// 样式缓存
|
||||
private GUIStyle _headerStyle;
|
||||
private GUIStyle _selectedStyle;
|
||||
@ -116,9 +126,61 @@ public class GuideConfigEditor : EditorWindow
|
||||
_entries = ParseJsonArray(json);
|
||||
_isDirty = false;
|
||||
_selectedIndex = -1;
|
||||
_presetsDirty = true;
|
||||
Debug.Log($"[GuideConfigEditor] 已加载 {_entries.Count} 条引导配置");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从所有现有条目中收集去重的预设值,用于快速选择
|
||||
/// </summary>
|
||||
private void RebuildPresets()
|
||||
{
|
||||
if (!_presetsDirty) return;
|
||||
_presetsDirty = false;
|
||||
|
||||
var satisfySet = new HashSet<string>();
|
||||
var triggerSet = new HashSet<string>();
|
||||
var endSet = new HashSet<string>();
|
||||
var roleExprSet = new HashSet<string>();
|
||||
var dialogPosSet = new HashSet<string>();
|
||||
var clickPosSet = new HashSet<string>();
|
||||
var focusPosSet = new HashSet<string>();
|
||||
|
||||
foreach (var e in _entries)
|
||||
{
|
||||
if (e.Conditions.Satisfy != null)
|
||||
foreach (var s in e.Conditions.Satisfy)
|
||||
if (!string.IsNullOrEmpty(s)) satisfySet.Add(s);
|
||||
|
||||
if (e.Conditions.Trigger != null)
|
||||
foreach (var t in e.Conditions.Trigger)
|
||||
if (!string.IsNullOrEmpty(t)) triggerSet.Add(t);
|
||||
|
||||
if (!string.IsNullOrEmpty(e.Conditions.End))
|
||||
endSet.Add(e.Conditions.End);
|
||||
|
||||
if (!string.IsNullOrEmpty(e.ShowUI.RoleExpression))
|
||||
roleExprSet.Add(e.ShowUI.RoleExpression);
|
||||
|
||||
if (!string.IsNullOrEmpty(e.ShowUI.DialogPos))
|
||||
dialogPosSet.Add(e.ShowUI.DialogPos);
|
||||
|
||||
if (!string.IsNullOrEmpty(e.ShowUI.ClickPos))
|
||||
clickPosSet.Add(e.ShowUI.ClickPos);
|
||||
|
||||
if (!string.IsNullOrEmpty(e.ShowUI.FocusPos))
|
||||
focusPosSet.Add(e.ShowUI.FocusPos);
|
||||
}
|
||||
|
||||
_satisfyPresets = satisfySet.OrderBy(s => s).ToList();
|
||||
_triggerPresets = triggerSet.OrderBy(s => s).ToList();
|
||||
_endPresets = endSet.OrderBy(s => s).ToList();
|
||||
_roleExpressionPresets = roleExprSet.OrderBy(s => s).ToList();
|
||||
_dialogPosPresets = dialogPosSet.OrderBy(s => s).ToList();
|
||||
_clickPosPresets = clickPosSet.OrderBy(s => s).ToList();
|
||||
_focusPosPresets = focusPosSet.OrderBy(s => s).ToList();
|
||||
}
|
||||
|
||||
private void SaveConfig()
|
||||
{
|
||||
string fullPath = Path.Combine(Application.dataPath, "..", ConfigPath);
|
||||
@ -136,6 +198,7 @@ public class GuideConfigEditor : EditorWindow
|
||||
private void OnGUI()
|
||||
{
|
||||
InitStyles();
|
||||
RebuildPresets();
|
||||
DrawToolbar();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
@ -201,7 +264,7 @@ public class GuideConfigEditor : EditorWindow
|
||||
}
|
||||
|
||||
GUILayout.Space(5);
|
||||
GUILayout.Label("搜索:", EditorStyles.toolbarButton, GUILayout.Width(30));
|
||||
GUILayout.Label("全局搜索:", EditorStyles.toolbarButton, GUILayout.Width(50));
|
||||
_searchText = EditorGUILayout.TextField(_searchText, EditorStyles.toolbarSearchField, GUILayout.Width(120));
|
||||
|
||||
GUILayout.Label("ID:", EditorStyles.toolbarButton, GUILayout.Width(20));
|
||||
@ -271,10 +334,10 @@ public class GuideConfigEditor : EditorWindow
|
||||
if (_showConditions)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
DrawStringList("Satisfy (满足条件)", ref entry.Conditions.Satisfy, ref _showSatisfy);
|
||||
DrawStringListWithPresets("Satisfy (满足条件)", ref entry.Conditions.Satisfy, ref _showSatisfy, _satisfyPresets);
|
||||
DrawField("CheckAgain", ref entry.Conditions.CheckAgain);
|
||||
DrawStringList("Trigger (触发器)", ref entry.Conditions.Trigger, ref _showTrigger);
|
||||
DrawField("End (结束条件)", ref entry.Conditions.End);
|
||||
DrawStringListWithPresets("Trigger (触发器)", ref entry.Conditions.Trigger, ref _showTrigger, _triggerPresets);
|
||||
DrawFieldWithPresets("End (结束条件)", ref entry.Conditions.End, _endPresets);
|
||||
DrawField("Delay (延迟秒)", ref entry.Conditions.Delay);
|
||||
DrawField("Limit (限制次数)", ref entry.Conditions.Limit);
|
||||
EditorGUI.indentLevel--;
|
||||
@ -288,12 +351,12 @@ public class GuideConfigEditor : EditorWindow
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
DrawField("LangText (多语言Key)", ref entry.ShowUI.LangText);
|
||||
DrawField("RoleExpression (角色表情)", ref entry.ShowUI.RoleExpression);
|
||||
DrawField("DialogPos (对话位置)", ref entry.ShowUI.DialogPos);
|
||||
DrawFieldWithPresets("RoleExpression (角色表情)", ref entry.ShowUI.RoleExpression, _roleExpressionPresets);
|
||||
DrawFieldWithPresets("DialogPos (对话位置)", ref entry.ShowUI.DialogPos, _dialogPosPresets);
|
||||
DrawField("OkBtn (确认按钮)", ref entry.ShowUI.OkBtn);
|
||||
DrawField("ClickSg (点击信号)", ref entry.ShowUI.ClickSg);
|
||||
DrawField("ClickPos (点击位置)", ref entry.ShowUI.ClickPos);
|
||||
DrawField("FocusPos (聚焦位置)", ref entry.ShowUI.FocusPos);
|
||||
DrawFieldWithPresets("ClickPos (点击位置)", ref entry.ShowUI.ClickPos, _clickPosPresets);
|
||||
DrawFieldWithPresets("FocusPos (聚焦位置)", ref entry.ShowUI.FocusPos, _focusPosPresets);
|
||||
DrawField("Phone (手机引导)", ref entry.ShowUI.Phone);
|
||||
DrawField("ClickToSkip (点击跳过)", ref entry.ShowUI.ClickToSkip);
|
||||
EditorGUI.indentLevel--;
|
||||
@ -324,6 +387,193 @@ public class GuideConfigEditor : EditorWindow
|
||||
if (newVal != (value ?? "")) { value = newVal; _isDirty = true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 带预设下拉的文本字段:点击▼按钮弹出已有值列表快速选择
|
||||
/// </summary>
|
||||
private void DrawFieldWithPresets(string label, ref string value, List<string> presets)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
string newVal = EditorGUILayout.TextField(label, value ?? "");
|
||||
if (newVal != (value ?? "")) { value = newVal; _isDirty = true; }
|
||||
|
||||
if (presets != null && presets.Count > 0)
|
||||
{
|
||||
if (GUILayout.Button("▼", GUILayout.Width(20)))
|
||||
{
|
||||
// 记录当前选中条目和字段信息,在回调中通过索引赋值
|
||||
int entryIdx = _selectedIndex;
|
||||
string fieldLabel = label;
|
||||
ShowPresetMenu(presets, selected =>
|
||||
{
|
||||
ApplyPresetToField(entryIdx, fieldLabel, selected);
|
||||
});
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 预设回调:根据字段标签将选中值写回对应字段
|
||||
/// </summary>
|
||||
private void ApplyPresetToField(int entryIdx, string fieldLabel, string selected)
|
||||
{
|
||||
if (entryIdx < 0 || entryIdx >= _entries.Count) return;
|
||||
var entry = _entries[entryIdx];
|
||||
|
||||
if (fieldLabel.StartsWith("End"))
|
||||
entry.Conditions.End = selected;
|
||||
else if (fieldLabel.StartsWith("RoleExpression"))
|
||||
entry.ShowUI.RoleExpression = selected;
|
||||
else if (fieldLabel.StartsWith("DialogPos"))
|
||||
entry.ShowUI.DialogPos = selected;
|
||||
else if (fieldLabel.StartsWith("ClickPos"))
|
||||
entry.ShowUI.ClickPos = selected;
|
||||
else if (fieldLabel.StartsWith("FocusPos"))
|
||||
entry.ShowUI.FocusPos = selected;
|
||||
|
||||
_isDirty = true;
|
||||
_presetsDirty = true;
|
||||
Repaint();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 带预设快速选择的字符串列表(Satisfy / Trigger)
|
||||
/// 点击 "+" 新增空行,点击 "▼" 从已有值中选择并新增
|
||||
/// </summary>
|
||||
private void DrawStringListWithPresets(string label, ref List<string> list, ref bool foldout, List<string> presets)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
foldout = EditorGUILayout.Foldout(foldout, $"{label} ({list.Count})", true);
|
||||
if (GUILayout.Button("+", GUILayout.Width(20)))
|
||||
{
|
||||
list.Add("");
|
||||
_isDirty = true;
|
||||
}
|
||||
// 预设快速添加按钮
|
||||
if (presets != null && presets.Count > 0)
|
||||
{
|
||||
if (GUILayout.Button("▼选择", GUILayout.Width(45)))
|
||||
{
|
||||
var listRef = list;
|
||||
ShowPresetMenu(presets, selected =>
|
||||
{
|
||||
listRef.Add(selected);
|
||||
_isDirty = true;
|
||||
_presetsDirty = true;
|
||||
Repaint();
|
||||
});
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (!foldout) return;
|
||||
|
||||
EditorGUI.indentLevel++;
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
string newVal = EditorGUILayout.TextField($"[{i}]", list[i] ?? "");
|
||||
if (newVal != (list[i] ?? "")) { list[i] = newVal; _isDirty = true; }
|
||||
|
||||
// 每行也有预设选择按钮,可以替换当前值
|
||||
if (presets != null && presets.Count > 0)
|
||||
{
|
||||
int idx = i;
|
||||
if (GUILayout.Button("▼", GUILayout.Width(20)))
|
||||
{
|
||||
var listRef = list;
|
||||
ShowPresetMenu(presets, selected =>
|
||||
{
|
||||
if (idx < listRef.Count)
|
||||
{
|
||||
listRef[idx] = selected;
|
||||
_isDirty = true;
|
||||
_presetsDirty = true;
|
||||
Repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (GUILayout.Button("↑", GUILayout.Width(20)) && i > 0)
|
||||
{
|
||||
(list[i - 1], list[i]) = (list[i], list[i - 1]);
|
||||
_isDirty = true;
|
||||
}
|
||||
if (GUILayout.Button("↓", GUILayout.Width(20)) && i < list.Count - 1)
|
||||
{
|
||||
(list[i], list[i + 1]) = (list[i + 1], list[i]);
|
||||
_isDirty = true;
|
||||
}
|
||||
if (GUILayout.Button("×", GUILayout.Width(20)))
|
||||
{
|
||||
list.RemoveAt(i);
|
||||
_isDirty = true;
|
||||
break;
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示预设值的下拉菜单(GenericMenu),按前缀分组
|
||||
/// </summary>
|
||||
private void ShowPresetMenu(List<string> presets, Action<string> onSelect)
|
||||
{
|
||||
var menu = new GenericMenu();
|
||||
// 按前缀分组显示
|
||||
var groups = new Dictionary<string, List<string>>();
|
||||
foreach (var p in presets)
|
||||
{
|
||||
string prefix = ExtractPrefix(p);
|
||||
if (!groups.ContainsKey(prefix))
|
||||
groups[prefix] = new List<string>();
|
||||
groups[prefix].Add(p);
|
||||
}
|
||||
|
||||
// 如果分组数 > 1 且有明显前缀,按分组显示
|
||||
if (groups.Count > 1 && groups.Keys.Any(k => k != ""))
|
||||
{
|
||||
foreach (var kvp in groups.OrderBy(g => g.Key))
|
||||
{
|
||||
string groupLabel = string.IsNullOrEmpty(kvp.Key) ? "其他" : kvp.Key;
|
||||
foreach (var item in kvp.Value)
|
||||
{
|
||||
// 用 \u2215 (除号斜杠) 替换 / 避免被 GenericMenu 当作子菜单分隔符
|
||||
string safeItem = item.Replace("/", "\u2215");
|
||||
string menuPath = $"{groupLabel}/{safeItem}";
|
||||
string captured = item;
|
||||
menu.AddItem(new GUIContent(menuPath), false, () => onSelect(captured));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 数量少或无明显前缀,平铺显示
|
||||
foreach (var item in presets)
|
||||
{
|
||||
string safeItem = item.Replace("/", "\u2215");
|
||||
string captured = item;
|
||||
menu.AddItem(new GUIContent(safeItem), false, () => onSelect(captured));
|
||||
}
|
||||
}
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提取条件字符串的前缀(如 "LevelEqual_1" → "LevelEqual","UIPanel_MainHomeUI_1" → "UIPanel")
|
||||
/// </summary>
|
||||
private string ExtractPrefix(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) return "";
|
||||
int idx = value.IndexOf('_');
|
||||
if (idx > 0 && idx < value.Length - 1)
|
||||
return value.Substring(0, idx);
|
||||
return "";
|
||||
}
|
||||
|
||||
private void DrawStringList(string label, ref List<string> list, ref bool foldout)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
@ -373,15 +623,15 @@ public class GuideConfigEditor : EditorWindow
|
||||
{
|
||||
IEnumerable<GuideEntry> result = _entries;
|
||||
|
||||
// 按名称搜索
|
||||
// 全字段搜索(Name、Conditions、ShowUI 所有文本字段)
|
||||
if (!string.IsNullOrEmpty(_searchText))
|
||||
{
|
||||
string lower = _searchText.ToLower();
|
||||
result = result.Where(e => (e.Name ?? "").ToLower().Contains(lower));
|
||||
result = result.Where(e => EntryMatchesSearch(e, lower));
|
||||
}
|
||||
|
||||
// 按ID搜索
|
||||
if (!string.IsNullOrEmpty(_searchId) && int.TryParse(_searchId, out int searchIdVal))
|
||||
if (!string.IsNullOrEmpty(_searchId))
|
||||
{
|
||||
result = result.Where(e => e.Id.ToString().Contains(_searchId));
|
||||
}
|
||||
@ -403,6 +653,52 @@ public class GuideConfigEditor : EditorWindow
|
||||
return result.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 全字段匹配:搜索 Name、Conditions(Satisfy/Trigger/End)、ShowUI 所有文本字段
|
||||
/// </summary>
|
||||
private bool EntryMatchesSearch(GuideEntry e, string lower)
|
||||
{
|
||||
// 基本信息
|
||||
if ((e.Name ?? "").ToLower().Contains(lower))
|
||||
return true;
|
||||
|
||||
// Conditions
|
||||
if (e.Conditions.Satisfy != null && e.Conditions.Satisfy.Any(s => (s ?? "").ToLower().Contains(lower)))
|
||||
return true;
|
||||
if (e.Conditions.Trigger != null && e.Conditions.Trigger.Any(s => (s ?? "").ToLower().Contains(lower)))
|
||||
return true;
|
||||
if ((e.Conditions.End ?? "").ToLower().Contains(lower))
|
||||
return true;
|
||||
if (e.Conditions.CheckAgain.ToString().Contains(lower))
|
||||
return true;
|
||||
if (e.Conditions.Delay.ToString("G").ToLower().Contains(lower))
|
||||
return true;
|
||||
if (e.Conditions.Limit.ToString().Contains(lower))
|
||||
return true;
|
||||
|
||||
// ShowUI
|
||||
if ((e.ShowUI.LangText ?? "").ToLower().Contains(lower))
|
||||
return true;
|
||||
if ((e.ShowUI.RoleExpression ?? "").ToLower().Contains(lower))
|
||||
return true;
|
||||
if ((e.ShowUI.DialogPos ?? "").ToLower().Contains(lower))
|
||||
return true;
|
||||
if ((e.ShowUI.ClickPos ?? "").ToLower().Contains(lower))
|
||||
return true;
|
||||
if ((e.ShowUI.FocusPos ?? "").ToLower().Contains(lower))
|
||||
return true;
|
||||
if (e.ShowUI.OkBtn.ToString().Contains(lower))
|
||||
return true;
|
||||
if (e.ShowUI.ClickSg.ToString().Contains(lower))
|
||||
return true;
|
||||
if (e.ShowUI.Phone.ToString().Contains(lower))
|
||||
return true;
|
||||
if (e.ShowUI.ClickToSkip.ToString().Contains(lower))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void AddNewEntry()
|
||||
{
|
||||
int maxId = _entries.Count > 0 ? _entries.Max(e => e.Id) : 0;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user