diff --git a/Unity/配置表联动查看器/ConfigLinkViewer/ConfigLinkDatabase.cs b/Unity/配置表联动查看器/ConfigLinkViewer/ConfigLinkDatabase.cs index 06fecdd..35a9e13 100644 --- a/Unity/配置表联动查看器/ConfigLinkViewer/ConfigLinkDatabase.cs +++ b/Unity/配置表联动查看器/ConfigLinkViewer/ConfigLinkDatabase.cs @@ -12,6 +12,7 @@ public class ConfigTableInfo public string description; public List relations = new List(); public bool isExistInProject; + public string fileExtension = ".xlsx"; } [Serializable] @@ -24,13 +25,70 @@ public class FieldRelation public string description; } +public class ReverseRelation +{ + public string sourceTable; + public string sourceDisplayName; + public string fieldName; + public string description; + public string relationFormat; +} + +[Serializable] +public class FieldRelationBackup +{ + public string fieldName; + public string targetTable; + public string targetField; + public string relationFormat; + public string description; +} + public static class ConfigLinkDatabase { private static List _cachedData; private static HashSet _existingTableNames; + private static string _excelFolderPath; private const string CONFIG_PATH = "Resources/Resources_moved/config"; + public static void SetExcelFolderPath(string path) + { + _excelFolderPath = path; + PlayerPrefs.SetString("ConfigLinkExcelFolderPath", path); + } + + public static string GetExcelFolderPath() + { + if (string.IsNullOrEmpty(_excelFolderPath)) + { + _excelFolderPath = PlayerPrefs.GetString("ConfigLinkExcelFolderPath", ""); + } + return _excelFolderPath; + } + + public static string GetExcelFilePath(string tableName) + { + string folderPath = GetExcelFolderPath(); + if (string.IsNullOrEmpty(folderPath)) + return null; + + var tableInfo = GetTableInfo(tableName); + string extension = tableInfo?.fileExtension ?? ".xlsx"; + string excelPath = Path.Combine(folderPath, $"{tableName}{extension}"); + return File.Exists(excelPath) ? excelPath : null; + } + + public static bool IsExcelFolderSet() + { + return !string.IsNullOrEmpty(GetExcelFolderPath()); + } + + public static bool IsExcelFileExists(string tableName) + { + return GetExcelFilePath(tableName) != null; + } + public static List GetAllTableInfo() { if (_cachedData != null) @@ -319,7 +377,9 @@ public static class ConfigLinkDatabase }}, new ConfigTableInfo { tableName = "fishingreward", displayName = "钓鱼奖励表", description = "钓鱼游戏奖励", relations = new List { new FieldRelation { fieldName = "rewards", targetTable = "prop", targetField = "id", relationFormat = "type_id_num", description = "奖励" } - }} + }}, + new ConfigTableInfo { tableName = "dirty_words", displayName = "敏感词表", description = "敏感词过滤配置(TXT文件)", fileExtension = ".txt" }, + new ConfigTableInfo { tableName = "奖励格式说明", displayName = "奖励格式说明", description = "奖励数据格式说明文档(TXT文件)", fileExtension = ".txt" } }; MarkExistingTables(); @@ -341,12 +401,62 @@ public static class ConfigLinkDatabase } } + string configPathAlt = Path.Combine(Application.dataPath, "../Resources/Resources_moved/config"); + if (Directory.Exists(configPathAlt)) + { + string[] jsonFilesAlt = Directory.GetFiles(configPathAlt, "*.json"); + foreach (string file in jsonFilesAlt) + { + string tableName = Path.GetFileNameWithoutExtension(file); + _existingTableNames.Add(tableName.ToLower()); + } + } + foreach (var table in _cachedData) { - table.isExistInProject = _existingTableNames.Contains(table.tableName.ToLower()); + bool existInConfig = _existingTableNames.Contains(table.tableName.ToLower()); + + if (!existInConfig && table.fileExtension == ".txt") + { + string excelFolderPath = GetExcelFolderPath(); + if (!string.IsNullOrEmpty(excelFolderPath)) + { + string txtPath = Path.Combine(excelFolderPath, $"{table.tableName}{table.fileExtension}"); + existInConfig = File.Exists(txtPath); + } + } + + table.isExistInProject = existInConfig; } } + public static bool IsTableExistInConfigFolder(string tableName) + { + string configPath = Path.Combine(Application.dataPath, CONFIG_PATH); + if (!Directory.Exists(configPath)) + return false; + + var tableInfo = GetTableInfo(tableName); + string extension = tableInfo?.fileExtension ?? ".xlsx"; + + string jsonPath = Path.Combine(configPath, $"{tableName}.json"); + string configPathAlt = Path.Combine(Application.dataPath, "../Resources/Resources_moved/config"); + string jsonPathAlt = Path.Combine(configPathAlt, $"{tableName}.json"); + + if (extension == ".txt") + { + string excelFolderPath = GetExcelFolderPath(); + if (!string.IsNullOrEmpty(excelFolderPath)) + { + string txtPath = Path.Combine(excelFolderPath, $"{tableName}{extension}"); + if (File.Exists(txtPath)) + return true; + } + } + + return File.Exists(jsonPath) || File.Exists(jsonPathAlt); + } + public static ConfigTableInfo GetTableInfo(string tableName) { return GetAllTableInfo().Find(x => x.tableName.Equals(tableName, StringComparison.OrdinalIgnoreCase)); @@ -357,6 +467,90 @@ public static class ConfigLinkDatabase return GetAllTableInfo().ConvertAll(x => x.tableName); } + public static List GetReverseRelations(string targetTableName) + { + List result = new List(); + + foreach (var table in GetAllTableInfo()) + { + foreach (var relation in table.relations) + { + if (relation.targetTable.Equals(targetTableName, StringComparison.OrdinalIgnoreCase)) + { + result.Add(new ReverseRelation + { + sourceTable = table.tableName, + sourceDisplayName = table.displayName, + fieldName = relation.fieldName, + description = relation.description, + relationFormat = relation.relationFormat + }); + } + } + } + + return result; + } + + public static string FormatRelationValue(string format, string value) + { + if (string.IsNullOrEmpty(format) || string.IsNullOrEmpty(value)) + return value; + + string[] parts = value.Split('_'); + string result = ""; + + switch (format.ToLower()) + { + case "item_id_num": + if (parts.Length >= 3) + result = $"类型:{parts[0]}, ID:{parts[1]}, 数量:{parts[2]}"; + break; + case "type_id_num": + if (parts.Length >= 3) + result = $"类型:{parts[0]}, ID:{parts[1]}, 数量:{parts[2]}"; + break; + case "id_pos_lv": + if (parts.Length >= 3) + result = $"ID:{parts[0]}, 位置:{parts[1]}, 等级:{parts[2]}"; + break; + case "id_lv_num": + if (parts.Length >= 3) + result = $"ID:{parts[0]}, 等级:{parts[1]}, 数量:{parts[2]}"; + break; + case "id_lv_num_time": + if (parts.Length >= 4) + result = $"ID:{parts[0]}, 等级:{parts[1]}, 数量:{parts[2]}, 时间:{parts[3]}"; + break; + case "buffid_lv": + if (parts.Length >= 2) + result = $"BuffID:{parts[0]}, 等级:{parts[1]}"; + break; + case "rune_id_num": + if (parts.Length >= 2) + result = $"符文ID:{parts[0]}, 数量:{parts[1]}"; + break; + case "equip_id_num": + if (parts.Length >= 2) + result = $"装备ID:{parts[0]}, 数量:{parts[1]}"; + break; + case "表名": + result = $"表名: {value}"; + break; + } + + return string.IsNullOrEmpty(result) ? value : result; + } + + public static List GetRelatedTableNames(string tableName) + { + var tableInfo = GetTableInfo(tableName); + if (tableInfo == null) + return new List(); + + return tableInfo.relations.Select(r => r.targetTable).Distinct().ToList(); + } + public static List GetAllDisplayNames() { return GetAllTableInfo().ConvertAll(x => $"{x.displayName} ({x.tableName})"); diff --git a/Unity/配置表联动查看器/ConfigLinkViewer/ConfigLinkViewerWindow.cs b/Unity/配置表联动查看器/ConfigLinkViewer/ConfigLinkViewerWindow.cs index ff48c1c..04b108d 100644 --- a/Unity/配置表联动查看器/ConfigLinkViewer/ConfigLinkViewerWindow.cs +++ b/Unity/配置表联动查看器/ConfigLinkViewer/ConfigLinkViewerWindow.cs @@ -2,33 +2,33 @@ using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.Linq; +using System.IO; public class ConfigLinkViewerWindow : EditorWindow { private string selectedTableName; - private Vector2 scrollPos; private Dictionary expandedRelations = new Dictionary(); private Vector2 relationScrollPos; private string searchFilter = ""; private bool showOnlyExisting = true; + private bool showReverseRelations = false; [MenuItem("Tools/配置表联动查看器")] public static void ShowWindow() { - GetWindow("配置表联动"); + var window = GetWindow("配置表联动"); + window.minSize = new Vector2(900, 600); + window.position = new Rect(100, 100, 900, 600); } private void OnGUI() { + float windowWidth = position.width; + float windowHeight = position.height; + DrawHeader(); EditorGUILayout.Space(); - DrawTableSelector(); - EditorGUILayout.Space(); - - if (!string.IsNullOrEmpty(selectedTableName)) - { - DrawTableInfo(); - } + DrawTableSelector(windowWidth, windowHeight); } private void DrawHeader() @@ -39,7 +39,7 @@ public class ConfigLinkViewerWindow : EditorWindow EditorGUILayout.EndVertical(); } - private void DrawTableSelector() + private void DrawTableSelector(float windowWidth, float windowHeight) { EditorGUILayout.BeginVertical("box"); @@ -47,9 +47,12 @@ public class ConfigLinkViewerWindow : EditorWindow EditorGUILayout.BeginHorizontal(); searchFilter = EditorGUILayout.TextField("搜索:", searchFilter, EditorStyles.toolbarSearchField); - showOnlyExisting = EditorGUILayout.ToggleLeft("只显示当前项目存在的表", showOnlyExisting, GUILayout.Width(160)); + showOnlyExisting = EditorGUILayout.ToggleLeft("只显示当前项目存在的表", showOnlyExisting); EditorGUILayout.EndHorizontal(); + EditorGUILayout.Space(); + DrawExcelPathSettings(windowWidth); + var allTables = showOnlyExisting ? ConfigLinkDatabase.GetExistingTables() : ConfigLinkDatabase.GetAllTableInfo(); var filteredTables = string.IsNullOrEmpty(searchFilter) ? allTables @@ -57,28 +60,32 @@ public class ConfigLinkViewerWindow : EditorWindow t.tableName.Contains(searchFilter) || t.description.Contains(searchFilter)).ToList(); - var displayNames = filteredTables.Select(x => + var displayNames = filteredTables.Select(x => { string existMark = showOnlyExisting ? "" : (x.isExistInProject ? "[存在]" : "[缺失]"); return $"{x.displayName} ({x.tableName}) {existMark}"; }).ToArray(); - - var tableKeys = filteredTables.Select(x => x.tableName).ToArray(); - int selectedIndex = selectedTableName != null ? System.Array.IndexOf(tableKeys, selectedTableName) : 0; - selectedIndex = Mathf.Clamp(selectedIndex, 0, Mathf.Max(0, displayNames.Length - 1)); + var tableKeys = filteredTables.Select(x => x.tableName).ToArray(); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); - EditorGUILayout.BeginVertical(GUILayout.Width(200)); + + float leftPanelWidth = Mathf.Clamp(windowWidth * 0.3f, 180f, 300f); + float rightPanelWidth = windowWidth - leftPanelWidth - 20f; + float panelHeight = windowHeight - 150f; + + EditorGUILayout.BeginVertical(GUILayout.Width(leftPanelWidth), GUILayout.Height(panelHeight)); EditorGUILayout.LabelField($"配置表列表 ({filteredTables.Count}):", EditorStyles.miniBoldLabel); - relationScrollPos = EditorGUILayout.BeginScrollView(relationScrollPos, GUILayout.Height(300)); + relationScrollPos = EditorGUILayout.BeginScrollView(relationScrollPos, false, true, GUILayout.ExpandHeight(true)); + + GUIStyle listButtonStyle = new GUIStyle(EditorStyles.label) { wordWrap = false, stretchWidth = true }; for (int i = 0; i < displayNames.Length; i++) { bool isSelected = tableKeys[i] == selectedTableName; - GUIStyle buttonStyle = isSelected ? "Button" : "Label"; + GUIStyle buttonStyle = isSelected ? "Button" : listButtonStyle; if (GUILayout.Button(displayNames[i], buttonStyle)) { @@ -90,19 +97,22 @@ public class ConfigLinkViewerWindow : EditorWindow EditorGUILayout.EndScrollView(); EditorGUILayout.EndVertical(); - EditorGUILayout.BeginVertical("box", GUILayout.MinWidth(400)); - DrawCurrentTableInfo(); + EditorGUILayout.BeginVertical("box", GUILayout.Width(rightPanelWidth), GUILayout.Height(panelHeight)); + DrawCurrentTableInfo(rightPanelWidth); EditorGUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); } - private void DrawCurrentTableInfo() + private void DrawCurrentTableInfo(float panelWidth) { + EditorGUILayout.BeginScrollView(Vector2.zero, false, true, GUILayout.ExpandHeight(true)); + if (string.IsNullOrEmpty(selectedTableName)) { EditorGUILayout.LabelField("请从左侧选择一个配置表", EditorStyles.centeredGreyMiniLabel); + EditorGUILayout.EndScrollView(); return; } @@ -110,6 +120,7 @@ public class ConfigLinkViewerWindow : EditorWindow if (tableInfo == null) { EditorGUILayout.LabelField("未找到该表的配置信息", EditorStyles.helpBox); + EditorGUILayout.EndScrollView(); return; } @@ -121,25 +132,143 @@ public class ConfigLinkViewerWindow : EditorWindow EditorGUILayout.Space(); - if (tableInfo.relations.Count == 0) + DrawOpenExcelButtons(panelWidth); + + EditorGUILayout.Space(); + + showReverseRelations = EditorGUILayout.ToggleLeft("显示反向引用", showReverseRelations); + + EditorGUILayout.Space(); + + if (showReverseRelations) { - EditorGUILayout.LabelField("该表暂无联动关系", EditorStyles.miniLabel); + DrawReverseRelations(panelWidth); } else { - EditorGUILayout.LabelField($"联动关系 ({tableInfo.relations.Count}):", EditorStyles.boldLabel); - scrollPos = EditorGUILayout.BeginScrollView(scrollPos, GUILayout.Height(250)); - - foreach (var relation in tableInfo.relations) + if (tableInfo.relations.Count == 0) { - DrawRelation(relation); + EditorGUILayout.LabelField("该表暂无联动关系", EditorStyles.miniLabel); } + else + { + EditorGUILayout.LabelField($"联动关系 ({tableInfo.relations.Count}):", EditorStyles.boldLabel); - EditorGUILayout.EndScrollView(); + foreach (var relation in tableInfo.relations) + { + DrawRelation(relation, panelWidth); + } + } + } + + EditorGUILayout.EndScrollView(); + } + + private void DrawOpenExcelButtons(float panelWidth) + { + EditorGUILayout.BeginHorizontal(); + + string excelPath = ConfigLinkDatabase.GetExcelFilePath(selectedTableName); + bool canOpen = !string.IsNullOrEmpty(excelPath); + + if (canOpen) + { + if (GUILayout.Button("打开表格", GUILayout.Height(30), GUILayout.Width(panelWidth / 2 - 5))) + { + OpenExcelFile(selectedTableName); + } + } + else + { + EditorGUILayout.LabelField("未找到Excel文件", EditorStyles.helpBox, GUILayout.Height(30), GUILayout.Width(panelWidth / 2 - 5)); + } + + if (GUILayout.Button("批量打开关联表", GUILayout.Height(30), GUILayout.Width(panelWidth / 2 - 5))) + { + OpenRelatedTables(selectedTableName); + } + + EditorGUILayout.EndHorizontal(); + } + + private void OpenExcelFile(string tableName) + { + string excelPath = ConfigLinkDatabase.GetExcelFilePath(tableName); + if (!string.IsNullOrEmpty(excelPath) && File.Exists(excelPath)) + { + System.Diagnostics.Process.Start(excelPath); } } - private void DrawRelation(FieldRelation relation) + private void OpenRelatedTables(string tableName) + { + var relatedTables = ConfigLinkDatabase.GetRelatedTableNames(tableName); + foreach (var relatedTable in relatedTables) + { + OpenExcelFile(relatedTable); + } + } + + private void DrawReverseRelations(float panelWidth) + { + var reverseRels = ConfigLinkDatabase.GetReverseRelations(selectedTableName); + if (reverseRels.Count == 0) + { + EditorGUILayout.LabelField("暂无其他表引用该表", EditorStyles.miniLabel); + } + else + { + EditorGUILayout.LabelField($"反向引用 ({reverseRels.Count}):", EditorStyles.boldLabel); + + foreach (var rel in reverseRels) + { + bool sourceExist = ConfigLinkDatabase.IsTableExist(rel.sourceTable); + EditorGUILayout.BeginVertical("frameBox", GUILayout.Width(panelWidth - 10)); + + GUIStyle headerStyle = new GUIStyle(sourceExist ? EditorStyles.boldLabel : EditorStyles.helpBox) { wordWrap = false }; + EditorGUILayout.LabelField($"{rel.sourceDisplayName} ({rel.sourceTable}) → {rel.fieldName}", headerStyle); + + GUIStyle labelStyle = new GUIStyle(EditorStyles.miniLabel) { wordWrap = false }; + EditorGUILayout.LabelField($"说明: {rel.description}", labelStyle); + + if (!string.IsNullOrEmpty(rel.relationFormat)) + { + EditorGUILayout.LabelField($"格式: {rel.relationFormat}", labelStyle); + } + + if (sourceExist && GUILayout.Button($"查看 {rel.sourceTable} 表", EditorStyles.miniButton)) + { + selectedTableName = rel.sourceTable; + expandedRelations.Clear(); + } + + EditorGUILayout.EndVertical(); + EditorGUILayout.Space(2); + } + } + } + + private void DrawExcelPathSettings(float windowWidth) + { + string currentPath = ConfigLinkDatabase.GetExcelFolderPath(); + string displayPath = string.IsNullOrEmpty(currentPath) ? "未设置" : currentPath; + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Excel文件夹:", GUILayout.Width(70)); + EditorGUILayout.LabelField(displayPath, EditorStyles.textField, GUILayout.ExpandWidth(true)); + + if (GUILayout.Button("选择文件夹", GUILayout.Width(80))) + { + string selectedPath = EditorUtility.OpenFolderPanel("选择Excel文件夹", "", ""); + if (!string.IsNullOrEmpty(selectedPath)) + { + ConfigLinkDatabase.SetExcelFolderPath(selectedPath); + } + } + EditorGUILayout.EndHorizontal(); + } + + private void DrawRelation(FieldRelation relation, float panelWidth) { string key = $"{selectedTableName}_{relation.fieldName}_{relation.targetTable}"; if (!expandedRelations.ContainsKey(key)) @@ -150,29 +279,33 @@ public class ConfigLinkViewerWindow : EditorWindow bool targetExist = ConfigLinkDatabase.IsTableExist(relation.targetTable); string existMark = targetExist ? "" : "[目标表缺失]"; - EditorGUILayout.BeginVertical("frameBox"); + EditorGUILayout.BeginVertical("frameBox", GUILayout.Width(panelWidth - 10)); + + GUIStyle foldoutStyle = new GUIStyle(EditorStyles.foldout) { wordWrap = false, richText = true }; expandedRelations[key] = EditorGUILayout.Foldout(expandedRelations[key], $"{relation.fieldName} → {relation.targetTable}.{relation.targetField} {existMark}", - true); + true, foldoutStyle); if (expandedRelations[key]) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.Space(); - EditorGUILayout.BeginVertical("helpBox"); + EditorGUILayout.BeginVertical("helpBox", GUILayout.Width(panelWidth - 30)); - EditorGUILayout.LabelField($"目标表: {relation.targetTable} {(targetExist ? "" : "(当前项目不存在)")}", - targetExist ? EditorStyles.miniLabel : EditorStyles.helpBox); - EditorGUILayout.LabelField($"目标字段: {relation.targetField}", EditorStyles.miniLabel); + GUIStyle labelStyle = new GUIStyle(EditorStyles.miniLabel) { wordWrap = false, richText = true }; + + EditorGUILayout.LabelField($"目标表: {relation.targetTable} {(targetExist ? "" : "(当前项目不存在)")}", labelStyle); + EditorGUILayout.LabelField($"目标字段: {relation.targetField}", labelStyle); if (!string.IsNullOrEmpty(relation.relationFormat)) { - EditorGUILayout.LabelField($"格式: {relation.relationFormat}", EditorStyles.miniLabel); + EditorGUILayout.LabelField($"格式: {relation.relationFormat}", labelStyle); + DrawFormatExample(relation.relationFormat); } - EditorGUILayout.LabelField($"说明: {relation.description}", EditorStyles.miniLabel); + EditorGUILayout.LabelField($"说明: {relation.description}", labelStyle); - if (targetExist && GUILayout.Button($"查看 {relation.targetTable} 表", EditorStyles.miniButton, GUILayout.Width(120))) + if (targetExist && GUILayout.Button($"查看 {relation.targetTable} 表", EditorStyles.miniButton)) { selectedTableName = relation.targetTable; expandedRelations.Clear(); @@ -186,76 +319,48 @@ public class ConfigLinkViewerWindow : EditorWindow EditorGUILayout.Space(2); } - private void DrawTableInfo() + private void DrawFormatExample(string format) { - var tableInfo = ConfigLinkDatabase.GetTableInfo(selectedTableName); - if (tableInfo == null) return; + string example = ""; + string formatted = ""; - EditorGUILayout.BeginVertical("box"); - - EditorGUILayout.LabelField($"当前表: {tableInfo.displayName} ({tableInfo.tableName})", EditorStyles.boldLabel); - EditorGUILayout.LabelField($"描述: {tableInfo.description}", EditorStyles.miniLabel); - - EditorGUILayout.Space(); - - if (tableInfo.relations.Count == 0) + switch (format.ToLower()) { - EditorGUILayout.LabelField("暂无联动关系", EditorStyles.miniLabel); - } - else - { - EditorGUILayout.LabelField("联动关系:", EditorStyles.boldLabel); - - scrollPos = EditorGUILayout.BeginScrollView(scrollPos, GUILayout.Height(300)); - - foreach (var relation in tableInfo.relations) - { - DrawRelationItem(relation); - } - - EditorGUILayout.EndScrollView(); + case "item_id_num": + case "type_id_num": + example = "1001_10_5"; + formatted = "类型:1001, ID:10, 数量:5"; + break; + case "id_pos_lv": + example = "100_1_30"; + formatted = "ID:100, 位置:1, 等级:30"; + break; + case "id_lv_num": + example = "100_30_5"; + formatted = "ID:100, 等级:30, 数量:5"; + break; + case "id_lv_num_time": + example = "100_30_5_120"; + formatted = "ID:100, 等级:30, 数量:5, 时间:120秒"; + break; + case "buffid_lv": + example = "5_3"; + formatted = "BuffID:5, 等级:3"; + break; + case "rune_id_num": + example = "10_2"; + formatted = "符文ID:10, 数量:2"; + break; + case "equip_id_num": + example = "20_1"; + formatted = "装备ID:20, 数量:1"; + break; } - EditorGUILayout.EndVertical(); - } - - private void DrawRelationItem(FieldRelation relation) - { - string key = $"{selectedTableName}_{relation.fieldName}"; - if (!expandedRelations.ContainsKey(key)) + if (!string.IsNullOrEmpty(example)) { - expandedRelations[key] = true; - } - - bool targetExist = ConfigLinkDatabase.IsTableExist(relation.targetTable); - - expandedRelations[key] = EditorGUILayout.Foldout(expandedRelations[key], - $"字段: {relation.fieldName} → {relation.targetTable} {(targetExist ? "" : "(缺失)")}"); - - if (expandedRelations[key]) - { - EditorGUILayout.BeginHorizontal(); - EditorGUILayout.Space(); - - EditorGUILayout.BeginVertical(); - EditorGUILayout.LabelField($"目标字段: {relation.targetField}", EditorStyles.miniLabel); - - if (!string.IsNullOrEmpty(relation.relationFormat)) - { - EditorGUILayout.LabelField($"数据格式: {relation.relationFormat}", EditorStyles.miniLabel); - } - - EditorGUILayout.LabelField($"说明: {relation.description}", EditorStyles.miniLabel); - - if (targetExist && GUILayout.Button($"跳转到 {relation.targetTable}", EditorStyles.miniButton)) - { - selectedTableName = relation.targetTable; - Repaint(); - return; - } - - EditorGUILayout.EndVertical(); - EditorGUILayout.EndHorizontal(); + GUIStyle labelStyle = new GUIStyle(EditorStyles.miniLabel) { wordWrap = false, richText = true }; + EditorGUILayout.LabelField($"示例: {example} → {formatted}", labelStyle); } } } diff --git a/Unity/配置表联动查看器/README.md b/Unity/配置表联动查看器/README.md index c1acac2..d6b1a63 100644 --- a/Unity/配置表联动查看器/README.md +++ b/Unity/配置表联动查看器/README.md @@ -1,144 +1,159 @@ -# 配置表联动查看器 +# 配置表联动关系查看器 -一个用于 Unity 项目的配置表联动关系可视化工具,支持跨项目复用。 +一个用于查看 Unity 项目中配置表联动关系的编辑器工具,支持跨项目复用。 ## 功能特性 -- ✅ 支持 76+ 配置表的联动关系查看 -- ✅ 自动检测项目中实际存在的配置表 -- ✅ 智能搜索功能 -- ✅ 快速跳转到关联表 -- ✅ 显示数据格式说明 -- ✅ 跨项目自动适配 +### 1. 配置表列表 +- 支持搜索过滤配置表 +- 显示表是否存在于当前项目 +- 支持 TXT 文件(如敏感词表) + +### 2. 联动关系查看 +- 查看当前表关联哪些其他表 +- 显示字段联动关系详情 +- 支持跳转到关联表 + +### 3. 反向查询 +- 查看哪些表引用了当前表 +- 快速定位依赖关系 + +### 4. 数据格式可视化 +- 自动解析数据格式 +- 显示格式示例和说明 +- 支持多种格式:item_id_num, id_pos_lv, id_lv_num 等 + +### 5. Excel 操作 +- 一键打开配置表 Excel 文件 +- 批量打开所有关联表 + +### 6. 跨项目适配 +- 自动检测项目中存在的配置表 +- 支持不同项目配置不同路径 ## 安装指南 ### 方法一:复制文件 +1. 复制 `ConfigLinkViewer` 文件夹到目标项目 +2. 路径:`Assets/Editor/ConfigLinkViewer/` -1. 找到本项目中的 `ConfigLinkViewer` 文件夹: - ``` - Assets/Scripts/mgame/tools/ConfigLinkViewer/ - ``` - -2. 复制整个文件夹到目标项目的任意位置(建议保持相同路径): - ``` - 目标项目/Assets/Scripts/mgame/tools/ConfigLinkViewer/ - ``` - -3. 打开 Unity,等待脚本编译完成。 - -### 方法二:Git 子模块(推荐) - +### 方法二:Git 子模块 ```bash -cd 你的项目目录 -git submodule add <本工具仓库地址> Assets/Scripts/mgame/tools/ConfigLinkViewer +git submodule add Assets/Editor/ConfigLinkViewer ``` ## 使用说明 ### 打开工具 +在 Unity 编辑器菜单中点击:`Tools → 配置表联动查看器` -在 Unity 编辑器菜单中点击: -``` -Tools → 配置表联动查看器 -``` +### 设置 Excel 路径 +1. 点击"选择文件夹"按钮 +2. 选择包含配置表 Excel 文件的文件夹 +3. 路径会自动保存 -### 界面说明 - -| 区域 | 说明 | +### 基本操作 +| 操作 | 说明 | |------|------| -| 搜索框 | 按表名、显示名、描述搜索配置表 | -| 过滤选项 | 可选择只显示当前项目存在的表 | -| 左侧列表 | 配置表列表,显示状态标记 | -| 右侧详情 | 显示选中表的联动关系 | +| 单击列表项 | 选中配置表,查看详情 | +| 点击"打开表格" | 打开当前表的 Excel 文件 | +| 点击"批量打开关联表" | 打开所有关联的配置表 | +| 勾选"显示反向引用" | 查看哪些表引用了当前表 | -### 操作指南 - -1. **选择配置表**:从左侧列表点击任意配置表 -2. **查看联动关系**:右侧显示该表的所有字段关联 -3. **展开详情**:点击折叠箭头查看详细信息 -4. **跳转到关联表**:点击"查看 xxx 表"按钮跳转 -5. **搜索过滤**:在搜索框输入关键词快速定位 - -### 状态标识说明 - -| 标记 | 含义 | -|------|------| -| [存在] | 该表存在于当前项目 | -| [缺失] | 该表不存在于当前项目 | -| [目标表缺失] | 联动目标表不存在于当前项目 | - -## 跨项目适配 - -工具会自动检测目标项目中的配置表: - -1. 扫描路径:`Resources/Resources_moved/config/*.json` -2. 自动标记哪些表存在/缺失 -3. 缺失的表不会影响工具正常运行 -4. 联动到缺失表的跳转按钮会自动禁用 - -## 配置表联动关系示例 - -### 敌人表 (enemy) -- reward_id → checkpointreward.id - 掉落奖励 -- skills → skill.id - 技能列表 - -### 商店表 (mall) -- productId → prop.id - 商品道具ID -- consumes → prop.id (格式: item_id_num) - 购买消耗 -- rewards → prop.id (格式: item_id_num) - 购买获得奖励 - -### 英雄表 (hero) -- skills → skill.id - 技能列表 -- equips → equip.id - 装备列表 -- buffs → buff.id - 升阶解锁buff - -## 数据格式说明 +### 数据格式说明 | 格式 | 示例 | 说明 | |------|------|------| -| item_id_num | 1001_5 | 道具ID_数量 | -| type_id_num | item_1001_5 | 类型_道具ID_数量 | -| id_pos_lv | 101_2_5 | ID_位置_等级 | -| id_lv_num | 101_5_3 | ID_等级_数量 | -| id_lv_num_time | 101_5_3_30 | ID_等级_数量_时间 | +| item_id_num | 1001_10_5 | 类型:1001, ID:10, 数量:5 | +| type_id_num | 1001_10_5 | 类型:1001, ID:10, 数量:5 | +| id_pos_lv | 100_1_30 | ID:100, 位置:1, 等级:30 | +| id_lv_num | 100_30_5 | ID:100, 等级:30, 数量:5 | +| id_lv_num_time | 100_30_5_120 | ID:100, 等级:30, 数量:5, 时间:120秒 | +| buffid_lv | 5_3 | BuffID:5, 等级:3 | +| rune_id_num | 10_2 | 符文ID:10, 数量:2 | +| equip_id_num | 20_1 | 装备ID:20, 数量:1 | -## 维护指南 +## 跨项目复用 -### 添加新表联动关系 +### 自动检测 +工具会自动检测目标项目中的配置表: +- 检查 `Resources/Resources_moved/config/` 目录 +- 检查 `Resources/config/` 目录 +- TXT 文件检查 Excel 文件夹 -编辑 `ConfigLinkDatabase.cs`,在 `GetAllTableInfo()` 方法中添加: +### 添加新配置表 +编辑 `ConfigLinkDatabase.cs`,在 `GetAllTableInfo()` 方法中添加新表: ```csharp new ConfigTableInfo { - tableName = "新表名", - displayName = "显示名称", - description = "表描述", + tableName = "new_table", + displayName = "新表", + description = "新表描述", relations = new List { new FieldRelation { - fieldName = "字段名", - targetTable = "目标表名", - targetField = "目标字段", - relationFormat = "数据格式(可选)", - description = "说明文字" + fieldName = "field1", + targetTable = "target_table", + targetField = "id", + description = "关联说明" } } } ``` -### 清理缓存 +## 文件结构 -如果配置表有变动,可以调用: - -```csharp -ConfigLinkDatabase.ClearCache(); +``` +Assets/Editor/ConfigLinkViewer/ +├── ConfigLinkDatabase.cs # 配置表数据和联动关系存储 +├── ConfigLinkViewerWindow.cs # Unity编辑器窗口界面 +└── README.md # 使用文档 ``` -## 技术支持 +## 支持的配置表 -如有问题或建议,请联系开发者。 +工具包含以下类型的配置表支持: +- 活动系统:activity, activityreward +- 成就系统:achievement +- 战斗系统:fight_fb1~4, fight_arena, fight_sample +- 英雄系统:hero, herolevel, herostage +- 道具系统:prop, mall +- 装备系统:equip, equiplevel, equipstage +- 技能系统:skill, skilllevel +- 符文系统:rune, runelevel +- 任务系统:quest +- VIP系统:vip, recharge +- 服务端文件:dirty_words.txt, 奖励格式说明.txt ---- +## 注意事项 -*版本: 1.0* -*最后更新: 2026年5月* +1. Excel 文件命名必须与配置表名一致(如 `enemy.xlsx`) +2. 路径设置后会自动保存到 PlayerPrefs +3. 建议将 Excel 文件夹设置为版本控制忽略 +4. TXT 文件需要放置在 Excel 文件夹中 + +## 更新日志 + +### v1.0.0 +- 初始版本 +- 支持配置表列表和联动关系查看 + +### v1.1.0 +- 添加反向查询功能 +- 添加数据格式可视化 +- 添加批量打开关联表功能 + +### v1.2.0 +- 支持 TXT 文件识别 +- 优化跨项目适配 +- 更新使用文档 + +### v1.3.0 +- 移除所有水平滚动条 +- 左右面板自适应布局 +- Excel文件夹路径与搜索框对齐 +- 文本保持单行显示不换行 +- 窗口默认大小优化为 900×600 + +## 许可证 + +MIT License