AlicizaX/Client/Assets/InputGlyph/InputGlyphDatabaseEditor.cs
2026-03-11 13:04:31 +08:00

710 lines
30 KiB
C#
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.

using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(InputGlyphDatabase))]
public class InputGlyphDatabaseEditor : Editor
{
SerializedProperty tablesProp;
SerializedProperty placeholderSpriteProp;
InputGlyphDatabase db;
int tabIndex = 0;
bool showAddField = false;
string newTableName = "";
List<string> searchStrings = new List<string>();
List<int> currentPages = new List<int>();
// 每个表的临时字段,用于添加单个条目(目前仅支持 sprite
List<Sprite> newEntrySprites = new List<Sprite>();
const int itemsPerPage = 10;
const int previewSize = 52;
void OnEnable()
{
db = target as InputGlyphDatabase;
tablesProp = serializedObject.FindProperty("tables");
placeholderSpriteProp = serializedObject.FindProperty("placeholderSprite");
if (tablesProp == null)
{
Debug.LogError("Could not find serialized property 'tables' on InputGlyphDatabase. Check field name.");
return;
}
EnsureDefaultTable("Keyboard");
EnsureDefaultTable("Xbox");
EnsureDefaultTable("PlayStation");
SyncEditorListsWithTables();
}
public override void OnInspectorGUI()
{
serializedObject.Update();
if (db == null || tablesProp == null) return;
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.Space(4);
if (GUILayout.Button("Save Asset", EditorStyles.toolbarButton))
{
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(db);
AssetDatabase.SaveAssets();
}
GUILayout.FlexibleSpace();
if (GUILayout.Button(showAddField ? "Cancel +" : "+ Add Table", EditorStyles.toolbarButton, GUILayout.Width(110)))
{
showAddField = !showAddField;
newTableName = "";
}
int settingsIndex = tablesProp != null ? tablesProp.arraySize : 0;
bool settingsSelected = (tabIndex == settingsIndex);
if (GUILayout.Toggle(settingsSelected, "Settings", EditorStyles.toolbarButton, GUILayout.Width(90)) != settingsSelected)
{
tabIndex = (tabIndex == settingsIndex) ? 0 : settingsIndex;
}
EditorGUILayout.EndHorizontal();
if (showAddField)
{
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.Label("Name:", GUILayout.Width(40));
newTableName = EditorGUILayout.TextField(newTableName);
if (GUILayout.Button("Add", EditorStyles.toolbarButton, GUILayout.Width(80)))
{
string trimmed = newTableName != null ? newTableName.Trim() : "";
if (string.IsNullOrEmpty(trimmed))
{
EditorUtility.DisplayDialog("Invalid Name", "Table name cannot be empty.", "OK");
}
else
{
bool exists = false;
for (int i = 0; i < tablesProp.arraySize; ++i)
{
var t = tablesProp.GetArrayElementAtIndex(i);
var nameProp = t.FindPropertyRelative("deviceName");
if (nameProp != null && string.Equals(nameProp.stringValue, trimmed, StringComparison.OrdinalIgnoreCase))
{
exists = true;
break;
}
}
if (exists)
{
EditorUtility.DisplayDialog("Duplicate", "A table with that name already exists.", "OK");
}
else
{
int newIndex = tablesProp.arraySize;
tablesProp.InsertArrayElementAtIndex(newIndex);
var newTable = tablesProp.GetArrayElementAtIndex(newIndex);
var nameProp = newTable.FindPropertyRelative("deviceName");
if (nameProp != null) nameProp.stringValue = trimmed;
var sheetProp = newTable.FindPropertyRelative("spriteSheetTexture");
if (sheetProp != null) sheetProp.objectReferenceValue = null;
var entriesProp = newTable.FindPropertyRelative("entries");
if (entriesProp != null) entriesProp.arraySize = 0;
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(db);
SyncEditorListsWithTables();
showAddField = false;
tabIndex = tablesProp.arraySize - 1;
}
}
}
if (GUILayout.Button("Cancel", EditorStyles.toolbarButton, GUILayout.Width(80)))
{
showAddField = false;
newTableName = "";
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.Space(6);
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
int tablesCount = tablesProp.arraySize;
for (int i = 0; i < tablesCount; ++i)
{
var t = tablesProp.GetArrayElementAtIndex(i);
var nameProp = t.FindPropertyRelative("deviceName");
string name = nameProp != null ? nameProp.stringValue : ("Table " + i);
bool selected = (tabIndex == i);
if (GUILayout.Toggle(selected, name, EditorStyles.toolbarButton, GUILayout.MinWidth(60)))
{
tabIndex = i;
}
if (GUILayout.Button("×", EditorStyles.toolbarButton, GUILayout.Width(22)))
{
if (EditorUtility.DisplayDialog("Delete Table?",
$"Delete table '{name}' and all its entries? This cannot be undone.", "Delete", "Cancel"))
{
tablesProp.DeleteArrayElementAtIndex(i);
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(db);
SyncEditorListsWithTables();
tabIndex = Mathf.Clamp(tabIndex, 0, Math.Max(0, tablesProp.arraySize - 1));
return;
}
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(8);
EditorGUILayout.BeginVertical("box");
if (tabIndex == tablesProp.arraySize)
{
// 设置
EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel);
EditorGUILayout.Space(4);
EditorGUILayout.PropertyField(placeholderSpriteProp, new GUIContent("Placeholder Sprite"));
Sprite placeholder = placeholderSpriteProp.objectReferenceValue as Sprite;
EditorGUILayout.Space(6);
EditorGUILayout.LabelField("Preview", EditorStyles.miniBoldLabel);
if (placeholder != null)
{
Texture2D preview = AssetPreview.GetAssetPreview(placeholder);
if (preview == null) preview = AssetPreview.GetMiniThumbnail(placeholder);
if (preview != null) GUILayout.Label(preview, GUILayout.Width(previewSize), GUILayout.Height(previewSize));
else EditorGUILayout.ObjectField(placeholder, typeof(Sprite), false, GUILayout.Width(previewSize), GUILayout.Height(previewSize));
}
else
{
EditorGUILayout.HelpBox("No placeholder sprite assigned. If FindEntryByControlPath receives an empty path, it will return null.", MessageType.Info);
}
}
else
{
if (tabIndex < 0 || tabIndex >= tablesProp.arraySize)
{
EditorGUILayout.HelpBox("Invalid table index.", MessageType.Error);
}
else
{
var tableProp = tablesProp.GetArrayElementAtIndex(tabIndex);
EnsureEditorListsLength();
// 计算此表的 deviceName 和运行时索引(用于删除单个条目时)
var nameProp = tableProp.FindPropertyRelative("deviceName");
string deviceName = nameProp != null ? nameProp.stringValue : "";
int runtimeTableIndex = MapSerializedTableToRuntimeIndex(deviceName);
GUILayout.BeginHorizontal();
GUIStyle searchStyle = EditorStyles.toolbarSearchField ?? EditorStyles.textField;
searchStrings[tabIndex] = GUILayout.TextField(searchStrings[tabIndex] ?? "", searchStyle);
GUILayout.EndHorizontal();
EditorGUILayout.Space(6);
var sheetProp = tableProp.FindPropertyRelative("spriteSheetTexture");
EditorGUILayout.BeginHorizontal();
GUILayout.Label("Sprite Sheet (Texture2D)", GUILayout.Width(140));
EditorGUILayout.PropertyField(sheetProp, GUIContent.none, GUILayout.ExpandWidth(true));
if (GUILayout.Button("Parse Sprite Sheet", GUILayout.Width(120)))
{
ParseSpriteSheetIntoTableSerialized(tableProp);
}
if (GUILayout.Button("Clear", GUILayout.Width(80)))
{
var entriesProp = tableProp.FindPropertyRelative("entries");
if (entriesProp != null) entriesProp.arraySize = 0;
if (runtimeTableIndex >= 0 && db != null)
{
var table = db.tables[runtimeTableIndex];
if (table != null) table.entries.Clear();
}
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(db);
currentPages[tabIndex] = 0;
}
EditorGUILayout.EndHorizontal();
var platformProp = tableProp.FindPropertyRelative("platformIcons");
EditorGUILayout.PropertyField(platformProp, new GUIContent("Platforms Icons"));
Sprite placeholder = platformProp.objectReferenceValue as Sprite;
EditorGUILayout.Space(6);
EditorGUILayout.LabelField("Preview", EditorStyles.miniBoldLabel);
if (placeholder != null)
{
Texture2D preview = AssetPreview.GetAssetPreview(placeholder);
if (preview == null) preview = AssetPreview.GetMiniThumbnail(placeholder);
if (preview != null) GUILayout.Label(preview, GUILayout.Width(previewSize), GUILayout.Height(previewSize));
else EditorGUILayout.ObjectField(placeholder, typeof(Sprite), false, GUILayout.Width(previewSize), GUILayout.Height(previewSize));
}
else
{
EditorGUILayout.HelpBox("No PlatformIcons.", MessageType.Info);
}
EditorGUILayout.Space(6);
// ---- 新增:单个新增 Entry 的 UI只支持 Sprite ----
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField("Add Single Entry", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
GUILayout.Label("Sprite", GUILayout.Width(50));
newEntrySprites[tabIndex] = (Sprite)EditorGUILayout.ObjectField(newEntrySprites[tabIndex], typeof(Sprite), false, GUILayout.Width(80), GUILayout.Height(80));
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(6);
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Add Entry", GUILayout.Width(110)))
{
if (newEntrySprites[tabIndex] == null)
{
EditorUtility.DisplayDialog("Missing Sprite", "Please assign a Sprite to add.", "OK");
}
else
{
AddEntryToTableSerialized(tableProp, newEntrySprites[tabIndex]);
// reset temp field
newEntrySprites[tabIndex] = null;
currentPages[tabIndex] = 0;
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
// ---- end add-single-entry UI ----
EditorGUILayout.Space(6);
var entries = tableProp.FindPropertyRelative("entries");
if (entries != null)
{
int total = entries.arraySize;
List<int> matchedIndices = new List<int>();
string query = (searchStrings[tabIndex] ?? "").Trim();
for (int i = 0; i < total; ++i)
{
var eProp = entries.GetArrayElementAtIndex(i);
if (eProp == null) continue;
var spriteProp = eProp.FindPropertyRelative("Sprite");
Sprite s = spriteProp.objectReferenceValue as Sprite;
string name = s != null ? s.name : "";
if (string.IsNullOrEmpty(query) || name.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0)
{
matchedIndices.Add(i);
}
}
int matchedTotal = matchedIndices.Count;
int totalPages = Mathf.Max(1, (matchedTotal + itemsPerPage - 1) / itemsPerPage);
currentPages[tabIndex] = Mathf.Clamp(currentPages[tabIndex], 0, totalPages - 1);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("<<", EditorStyles.miniButtonLeft, GUILayout.Width(36)))
{
currentPages[tabIndex] = 0;
}
if (GUILayout.Button("<", EditorStyles.miniButtonMid, GUILayout.Width(36)))
{
currentPages[tabIndex] = Mathf.Max(0, currentPages[tabIndex] - 1);
}
GUILayout.FlexibleSpace();
EditorGUILayout.LabelField(string.Format("Page {0}/{1}", currentPages[tabIndex] + 1, totalPages), GUILayout.Width(120));
GUILayout.FlexibleSpace();
if (GUILayout.Button(">", EditorStyles.miniButtonMid, GUILayout.Width(36)))
{
currentPages[tabIndex] = Mathf.Min(totalPages - 1, currentPages[tabIndex] + 1);
}
if (GUILayout.Button(">>", EditorStyles.miniButtonRight, GUILayout.Width(36)))
{
currentPages[tabIndex] = totalPages - 1;
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(4);
int start = currentPages[tabIndex] * itemsPerPage;
int end = Math.Min(start + itemsPerPage, matchedTotal);
for (int mi = start; mi < end; ++mi)
{
int i = matchedIndices[mi];
var eProp = entries.GetArrayElementAtIndex(i);
if (eProp == null) continue;
// 显示一个条目,右侧带有小的删除按钮
using (new EditorGUILayout.HorizontalScope("box"))
{
// 左侧:预览列
using (new EditorGUILayout.VerticalScope(GUILayout.Width(80)))
{
var spriteProp = eProp.FindPropertyRelative("Sprite");
Sprite s = spriteProp.objectReferenceValue as Sprite;
EditorGUILayout.LabelField(s != null ? s.name : "<missing>", EditorStyles.boldLabel);
if (s != null)
{
Texture2D preview = AssetPreview.GetAssetPreview(s);
if (preview == null) preview = AssetPreview.GetMiniThumbnail(s);
if (preview != null)
{
GUILayout.Label(preview, GUILayout.Width(previewSize), GUILayout.Height(previewSize));
}
else
{
EditorGUILayout.PropertyField(spriteProp, GUIContent.none, GUILayout.Width(previewSize), GUILayout.Height(previewSize));
}
}
else
{
EditorGUILayout.PropertyField(spriteProp, GUIContent.none, GUILayout.Width(previewSize), GUILayout.Height(previewSize));
}
}
// 中间:操作列
EditorGUILayout.BeginVertical();
var actionProp = eProp.FindPropertyRelative("action");
EditorGUILayout.Space(2);
EditorGUILayout.PropertyField(actionProp, GUIContent.none, GUILayout.ExpandWidth(true));
EditorGUILayout.EndVertical();
// 右侧:小的删除按钮
GUILayout.Space(6);
if (GUILayout.Button("×", EditorStyles.toolbarButton, GUILayout.Width(24)))
{
string spriteName = null;
var sProp = eProp.FindPropertyRelative("Sprite");
if (sProp != null) spriteName = (sProp.objectReferenceValue as Sprite)?.name;
if (EditorUtility.DisplayDialog("Remove Entry?",
$"Remove entry '{(string.IsNullOrEmpty(spriteName) ? "<missing>" : spriteName)}' from table '{deviceName}'?", "Remove", "Cancel"))
{
// 从序列化数组中移除
var entriesProp = tableProp.FindPropertyRelative("entries");
if (entriesProp != null && i >= 0 && i < entriesProp.arraySize)
{
entriesProp.DeleteArrayElementAtIndex(i);
// 应用后从运行时移除以保持两者同步
serializedObject.ApplyModifiedProperties();
}
// 从运行时列表中移除db.tables
if (runtimeTableIndex >= 0 && db != null && db.tables != null && runtimeTableIndex < db.tables.Count)
{
var runtimeTable = db.tables[runtimeTableIndex];
if (runtimeTable != null && i >= 0 && i < runtimeTable.entries.Count)
{
runtimeTable.entries.RemoveAt(i);
}
}
EditorUtility.SetDirty(db);
AssetDatabase.SaveAssets();
// 重置分页并返回,避免继续迭代已变更的序列化数组
currentPages[tabIndex] = 0;
return;
}
}
}
EditorGUILayout.Space(4);
}
if (matchedTotal == 0)
{
EditorGUILayout.HelpBox("No entries match the search.", MessageType.Info);
}
}
}
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space(6);
serializedObject.ApplyModifiedProperties();
}
void EnsureDefaultTable(string name)
{
if (tablesProp == null) return;
for (int i = 0; i < tablesProp.arraySize; ++i)
{
var t = tablesProp.GetArrayElementAtIndex(i);
var nameProp = t.FindPropertyRelative("deviceName");
if (nameProp != null && string.Equals(nameProp.stringValue, name, StringComparison.OrdinalIgnoreCase))
return;
}
int idx = tablesProp.arraySize;
tablesProp.InsertArrayElementAtIndex(idx);
var newTable = tablesProp.GetArrayElementAtIndex(idx);
var deviceNameProp = newTable.FindPropertyRelative("deviceName");
if (deviceNameProp != null) deviceNameProp.stringValue = name;
var sheetProp = newTable.FindPropertyRelative("spriteSheetTexture");
if (sheetProp != null) sheetProp.objectReferenceValue = null;
var entriesProp = newTable.FindPropertyRelative("entries");
if (entriesProp != null) entriesProp.arraySize = 0;
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(db);
}
void SyncEditorListsWithTables()
{
int count = tablesProp != null ? tablesProp.arraySize : 0;
if (searchStrings == null) searchStrings = new List<string>();
if (currentPages == null) currentPages = new List<int>();
if (newEntrySprites == null) newEntrySprites = new List<Sprite>();
while (searchStrings.Count < count) searchStrings.Add("");
while (currentPages.Count < count) currentPages.Add(0);
while (newEntrySprites.Count < count) newEntrySprites.Add(null);
while (searchStrings.Count > count) searchStrings.RemoveAt(searchStrings.Count - 1);
while (currentPages.Count > count) currentPages.RemoveAt(currentPages.Count - 1);
while (newEntrySprites.Count > count) newEntrySprites.RemoveAt(newEntrySprites.Count - 1);
}
void EnsureEditorListsLength()
{
if (tablesProp == null) return;
SyncEditorListsWithTables();
}
// ----- 新增:把单个 Sprite 加入到序列化表和 runtime 表 -----
void AddEntryToTableSerialized(SerializedProperty tableProp, Sprite sprite)
{
if (tableProp == null) return;
var entriesProp = tableProp.FindPropertyRelative("entries");
if (entriesProp == null) return;
int insertIndex = entriesProp.arraySize;
entriesProp.InsertArrayElementAtIndex(insertIndex);
var newE = entriesProp.GetArrayElementAtIndex(insertIndex);
if (newE != null)
{
var spriteProp = newE.FindPropertyRelative("Sprite");
var actionProp = newE.FindPropertyRelative("action");
if (spriteProp != null) spriteProp.objectReferenceValue = sprite;
// 保持 action 序列化不变(大多数项目无法在此处直接序列化 InputAction
if (actionProp != null)
{
try
{
actionProp.objectReferenceValue = null;
}
catch
{
}
}
}
serializedObject.ApplyModifiedProperties();
// 同时添加到运行时列表
var nameProp = tableProp.FindPropertyRelative("deviceName");
string deviceName = nameProp != null ? nameProp.stringValue : "";
int tableIndex = MapSerializedTableToRuntimeIndex(deviceName);
if (tableIndex >= 0 && db != null && db.tables != null && tableIndex < db.tables.Count)
{
var tableObj = db.tables[tableIndex];
GlyphEntry e = new GlyphEntry();
e.Sprite = sprite;
e.action = null; // runtime only: none provided here
tableObj.entries.Add(e);
}
EditorUtility.SetDirty(db);
AssetDatabase.SaveAssets();
}
// ----- Parse Sprite Sheet (Texture2D with Multiple) -----
// 只用名称匹配覆盖 Sprite不改变已有 action
void ParseSpriteSheetIntoTableSerialized(SerializedProperty tableProp)
{
if (tableProp == null) return;
var sheetProp = tableProp.FindPropertyRelative("spriteSheetTexture");
var tex = sheetProp != null ? sheetProp.objectReferenceValue as Texture2D : null;
if (tex == null)
{
Debug.LogWarning("[InputGlyphDatabase] spriteSheetTexture is null for table.");
return;
}
var nameProp = tableProp.FindPropertyRelative("deviceName");
string deviceName = nameProp != null ? nameProp.stringValue : "";
int tableIndex = MapSerializedTableToRuntimeIndex(deviceName);
if (tableIndex < 0)
{
Debug.LogError($"[InputGlyphDatabase] Could not map serialized table '{deviceName}' to runtime db.tables.");
return;
}
var tableObj = db.tables[tableIndex];
if (tableObj == null)
{
Debug.LogError($"[InputGlyphDatabase] Runtime table object is null for '{deviceName}'.");
return;
}
string path = AssetDatabase.GetAssetPath(tex);
if (string.IsNullOrEmpty(path))
{
Debug.LogWarning("[InputGlyphDatabase] Could not get asset path for texture.");
return;
}
var assets = AssetDatabase.LoadAllAssetsAtPath(path);
if (assets == null || assets.Length == 0)
{
Debug.LogWarning("[InputGlyphDatabase] No sub-assets found at path: " + path);
return;
}
// 收集 sprites按照文件内顺序你如果想按名字排序可以在这里加
List<Sprite> sprites = new List<Sprite>();
foreach (var a in assets)
{
if (a is Sprite sp) sprites.Add(sp);
}
var entriesProp = tableProp.FindPropertyRelative("entries");
if (entriesProp == null)
{
Debug.LogWarning("[InputGlyphDatabase] entries property not found on table.");
return;
}
// 构建序列化表名 -> 索引 映射(忽略大小写)
var serializedNameToIndex = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < entriesProp.arraySize; ++i)
{
var eProp = entriesProp.GetArrayElementAtIndex(i);
if (eProp == null) continue;
var sProp = eProp.FindPropertyRelative("Sprite");
var sRef = sProp != null ? sProp.objectReferenceValue as Sprite : null;
if (sRef != null && !serializedNameToIndex.ContainsKey(sRef.name))
{
serializedNameToIndex[sRef.name] = i;
}
}
// runtime 名称 -> 索引 映射
var runtimeNameToIndex = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < tableObj.entries.Count; ++i)
{
var re = tableObj.entries[i];
if (re != null && re.Sprite != null)
{
var rn = re.Sprite.name;
if (!runtimeNameToIndex.ContainsKey(rn))
runtimeNameToIndex[rn] = i;
}
}
int replaced = 0, added = 0;
foreach (var sp in sprites)
{
if (sp == null) continue;
string nm = sp.name;
// -- 序列化层:同名则替换 Sprite 引用(不触碰 action否则新增元素并把 action 设为 null --
if (serializedNameToIndex.TryGetValue(nm, out int sIndex))
{
var eProp = entriesProp.GetArrayElementAtIndex(sIndex);
if (eProp != null)
{
var spriteProp = eProp.FindPropertyRelative("Sprite");
if (spriteProp != null) spriteProp.objectReferenceValue = sp;
// 不修改 actionProp保持原有 action如果有的话
}
replaced++;
}
else
{
int insertIndex = entriesProp.arraySize;
entriesProp.InsertArrayElementAtIndex(insertIndex);
var newE = entriesProp.GetArrayElementAtIndex(insertIndex);
if (newE != null)
{
var spriteProp = newE.FindPropertyRelative("Sprite");
var actionProp = newE.FindPropertyRelative("action");
if (spriteProp != null) spriteProp.objectReferenceValue = sp;
if (actionProp != null) actionProp.objectReferenceValue = null; // 新增项 action 为空
}
serializedNameToIndex[nm] = insertIndex;
added++;
}
// -- 运行时层:同名则替换 Sprite否则新增 runtime entryaction 设 null保持之前 runtime entry 的 action 不变) --
if (runtimeNameToIndex.TryGetValue(nm, out int rIndex))
{
var runtimeEntry = tableObj.entries[rIndex];
if (runtimeEntry != null) runtimeEntry.Sprite = sp;
}
else
{
GlyphEntry ge = new GlyphEntry();
ge.Sprite = sp;
ge.action = null;
tableObj.entries.Add(ge);
runtimeNameToIndex[nm] = tableObj.entries.Count - 1;
}
}
// 应用并保存修改(序列化层与 runtime 层保持同步)
EditorUtility.SetDirty(db);
serializedObject.Update();
serializedObject.ApplyModifiedProperties();
AssetDatabase.SaveAssets();
Debug.Log($"[InputGlyphDatabase] Merged sprite sheet '{tex.name}' into table '{deviceName}'. spritesFound={sprites.Count}, replaced={replaced}, added={added}, totalEntries={tableObj.entries.Count}");
}
int MapSerializedTableToRuntimeIndex(string deviceName)
{
if (db == null || db.tables == null) return -1;
for (int ti = 0; ti < db.tables.Count; ++ti)
{
if (string.Equals(db.tables[ti].deviceName, deviceName, StringComparison.OrdinalIgnoreCase))
return ti;
}
return -1;
}
}