#if UNITY_EDITOR using UnityEngine; using UnityEditor; using ArtResource; using System.Linq; using System.IO; using System.Collections.Generic; namespace ArtTools { /// /// 美术资源路径自动填充工具 /// /// 功能: /// 1. 在保存SO时自动填充SpritePath和SpineAssetPath /// 2. 提供手动批量更新所有SO的工具 /// 3. 同步更新JSON配置文件 /// /// 使用方法: /// - 自动:在ArtTableSO的OnValidate或保存时调用FillResourcePaths /// - 手动:菜单 -> 美术工具 -> 更新所有美术资源路径 /// public static class ArtResourcePathFiller { private const string SO_ROOT_PATH = "Assets/Art_SubModule/Art_SO"; private const string JSON_ROOT_PATH = "Assets/Art_SubModule/Art_Json"; /// /// 自动填充单个SO的资源路径 /// 应该在ArtTableSO保存时调用 /// public static void FillResourcePaths(ArtTableSO table) { if (table == null || table.Items == null) return; bool hasChanges = false; foreach (var item in table.Items) { // 填充Sprite路径 if (item.Sprite != null) { string spritePath = AssetDatabase.GetAssetPath(item.Sprite); if (!string.IsNullOrEmpty(spritePath) && item.SpritePath != spritePath) { item.SpritePath = spritePath; hasChanges = true; } } else { // 引用为空时,清空路径 if (!string.IsNullOrEmpty(item.SpritePath)) { item.SpritePath = ""; hasChanges = true; } } // 填充Spine路径 if (item.SpineAsset != null) { string spinePath = AssetDatabase.GetAssetPath(item.SpineAsset); if (!string.IsNullOrEmpty(spinePath) && item.SpineAssetPath != spinePath) { item.SpineAssetPath = spinePath; hasChanges = true; } } else { // 引用为空时,清空路径 if (!string.IsNullOrEmpty(item.SpineAssetPath)) { item.SpineAssetPath = ""; hasChanges = true; } } } if (hasChanges) { EditorUtility.SetDirty(table); Debug.Log($"[ArtResourcePathFiller] 已更新资源路径: {table.TableName} ({table.TableId})"); // 同步到JSON SyncToJson(table); // 注意:不在这里生成Bytes,因为单个表更新会频繁触发 // 批量操作完成后统一生成Bytes } } /// /// 同步SO数据到JSON文件 /// private static void SyncToJson(ArtTableSO table) { try { string soPath = AssetDatabase.GetAssetPath(table); string relativePath = soPath.Replace(SO_ROOT_PATH, "").Replace(".asset", ".json"); string jsonPath = JSON_ROOT_PATH + relativePath; // 确保目录存在 string directory = Path.GetDirectoryName(jsonPath); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } // 创建JSON数据 var jsonData = new ArtTableJsonData { TableId = table.TableId, TableName = table.TableName, Items = table.Items.Select(item => new ArtItemJsonData { Id = item.Id, Name = item.Name, Desc = item.Desc, SpritePath = item.SpritePath, SpineAssetPath = item.SpineAssetPath, SpineAnimName = item.SpineAnimName }).ToList() }; string json = JsonUtility.ToJson(jsonData, true); File.WriteAllText(jsonPath, json); Debug.Log($"[ArtResourcePathFiller] JSON同步成功: {jsonPath}"); } catch (System.Exception ex) { Debug.LogError($"[ArtResourcePathFiller] JSON同步失败: {ex.Message}"); } } /// /// 生成合并的Thrift Bytes文件(调用ArtResourceConfigEditor的方法) /// public static void GenerateMergedThriftBytes() { try { // 通过反射调用ArtResourceConfigEditor的GenerateMergedThriftBytes方法 // 因为那是一个实例方法,我们需要找到编辑器窗口实例或使用静态版本 var editorType = System.Type.GetType("ArtTools.ArtResourceConfigEditor,Assembly-CSharp-Editor"); if (editorType != null) { var method = editorType.GetMethod("GenerateMergedThriftBytesStatic", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); if (method != null) { method.Invoke(null, null); } else { Debug.LogWarning("[ArtResourcePathFiller] 未找到GenerateMergedThriftBytesStatic方法,请手动运行【美术工具/Thrift/批量生成所有Bytes文件】"); } } } catch (System.Exception ex) { Debug.LogError($"[ArtResourcePathFiller] 生成Thrift Bytes失败: {ex.Message}"); } } /// /// 手动批量更新所有ArtTableSO的资源路径 /// [MenuItem("美术工具/路径管理/更新所有美术资源路径")] public static void UpdateAllArtTablePaths() { // 查找所有ArtTableSO string[] guids = AssetDatabase.FindAssets("t:ArtTableSO"); if (guids.Length == 0) { Debug.LogWarning("[ArtResourcePathFiller] 未找到任何ArtTableSO文件"); return; } int updatedCount = 0; int totalCount = guids.Length; EditorUtility.DisplayProgressBar("更新美术资源路径", "正在扫描...", 0); for (int i = 0; i < guids.Length; i++) { string path = AssetDatabase.GUIDToAssetPath(guids[i]); var table = AssetDatabase.LoadAssetAtPath(path); if (table != null) { EditorUtility.DisplayProgressBar( "更新美术资源路径", $"处理: {table.TableName} ({i + 1}/{totalCount})", (float)i / totalCount ); FillResourcePaths(table); updatedCount++; } } EditorUtility.ClearProgressBar(); // 保存所有修改 AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); // 生成合并的Thrift Bytes文件 EditorUtility.DisplayProgressBar("生成Thrift Bytes", "正在生成配置文件...", 0.9f); GenerateMergedThriftBytes(); EditorUtility.ClearProgressBar(); Debug.Log($"[ArtResourcePathFiller] ✓ 批量更新完成: 处理了 {updatedCount} 个ArtTableSO,已同步JSON配置和Thrift Bytes"); EditorUtility.DisplayDialog( "更新完成", $"已更新 {updatedCount} 个美术资源表的路径信息\n并同步更新了JSON配置文件和Thrift Bytes\n\n现在可以在Runtime模式下正常加载资源了", "确定" ); } /// /// 验证所有SO的路径完整性 /// [MenuItem("美术工具/路径管理/验证美术资源路径完整性")] public static void ValidateAllPaths() { string[] guids = AssetDatabase.FindAssets("t:ArtTableSO"); if (guids.Length == 0) { Debug.LogWarning("[ArtResourcePathFiller] 未找到任何ArtTableSO文件"); return; } int missingPathCount = 0; int totalItemCount = 0; foreach (string guid in guids) { string path = AssetDatabase.GUIDToAssetPath(guid); var table = AssetDatabase.LoadAssetAtPath(path); if (table == null || table.Items == null) continue; foreach (var item in table.Items) { totalItemCount++; // 检查Sprite if (item.Sprite != null && string.IsNullOrEmpty(item.SpritePath)) { Debug.LogWarning($"[{table.TableName}] 资源项 '{item.Name}' 有Sprite引用但缺少路径"); missingPathCount++; } // 检查Spine if (item.SpineAsset != null && string.IsNullOrEmpty(item.SpineAssetPath)) { Debug.LogWarning($"[{table.TableName}] 资源项 '{item.Name}' 有Spine引用但缺少路径"); missingPathCount++; } } } if (missingPathCount == 0) { Debug.Log($"[ArtResourcePathFiller] ✓ 验证通过: 所有 {totalItemCount} 个资源项的路径都完整"); EditorUtility.DisplayDialog( "验证通过", $"所有 {totalItemCount} 个资源项的路径都完整\n\n可以正常使用Runtime模式", "确定" ); } else { Debug.LogError($"[ArtResourcePathFiller] ✗ 发现 {missingPathCount} 个缺少路径的资源项"); EditorUtility.DisplayDialog( "发现问题", $"发现 {missingPathCount} 个缺少路径的资源项\n\n建议运行【美术工具 -> 更新所有美术资源路径】修复", "确定" ); } } } /// /// ArtTableSO的自定义Inspector /// 在Inspector底部添加"更新路径"按钮 /// [CustomEditor(typeof(ArtTableSO))] public class ArtTableSOInspector : Editor { public override void OnInspectorGUI() { // 绘制默认Inspector DrawDefaultInspector(); EditorGUILayout.Space(10); EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); EditorGUILayout.Space(5); var table = target as ArtTableSO; if (table == null) return; // 统计信息 int spriteCount = table.Items.Count(item => item.Sprite != null); int spineCount = table.Items.Count(item => item.SpineAsset != null); int missingPathCount = table.Items.Count(item => (item.Sprite != null && string.IsNullOrEmpty(item.SpritePath)) || (item.SpineAsset != null && string.IsNullOrEmpty(item.SpineAssetPath)) ); EditorGUILayout.BeginVertical(EditorStyles.helpBox); EditorGUILayout.LabelField("资源路径状态", EditorStyles.boldLabel); EditorGUILayout.LabelField($"Sprite数量: {spriteCount}"); EditorGUILayout.LabelField($"Spine数量: {spineCount}"); if (missingPathCount > 0) { var oldColor = GUI.contentColor; GUI.contentColor = Color.red; EditorGUILayout.LabelField($"⚠️ 缺少路径: {missingPathCount}"); GUI.contentColor = oldColor; } else if (spriteCount > 0 || spineCount > 0) { var oldColor = GUI.contentColor; GUI.contentColor = Color.green; EditorGUILayout.LabelField("✓ 路径完整"); GUI.contentColor = oldColor; } EditorGUILayout.EndVertical(); EditorGUILayout.Space(5); // 更新路径按钮 if (GUILayout.Button("🔄 更新此表的资源路径", GUILayout.Height(30))) { ArtResourcePathFiller.FillResourcePaths(table); EditorUtility.SetDirty(table); AssetDatabase.SaveAssets(); // 重新生成合并的Thrift Bytes ArtResourcePathFiller.GenerateMergedThriftBytes(); EditorUtility.DisplayDialog("更新完成", $"已更新 {table.TableName} 的资源路径\n并重新生成了Thrift Bytes配置", "确定"); } EditorGUILayout.Space(5); EditorGUILayout.HelpBox( "提示:保存SO时会自动更新路径,手动点击按钮可强制更新", MessageType.Info ); } } /// /// 资源保存时自动填充路径 /// public class ArtTableSOAssetPostprocessor : AssetPostprocessor { static void OnPostprocessAllAssets( string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { foreach (string assetPath in importedAssets) { // 只处理ArtTableSO if (!assetPath.EndsWith(".asset")) continue; var table = AssetDatabase.LoadAssetAtPath(assetPath); if (table != null) { ArtResourcePathFiller.FillResourcePaths(table); } } } } /// /// JSON数据结构 /// [System.Serializable] public class ArtTableJsonData { public int TableId; public string TableName; public List Items; } [System.Serializable] public class ArtItemJsonData { public int Id; public string Name; public string Desc; public string SpritePath; public string SpineAssetPath; public string SpineAnimName; } } #endif