#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