Art_SubModule/Editor/Art_Tools/ArtResourcePathFiller.cs
2026-02-04 16:43:33 +08:00

415 lines
15 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.

#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
using ArtResource;
using System.Linq;
using System.IO;
using System.Collections.Generic;
namespace ArtTools
{
/// <summary>
/// 美术资源路径自动填充工具
///
/// 功能:
/// 1. 在保存SO时自动填充SpritePath和SpineAssetPath
/// 2. 提供手动批量更新所有SO的工具
/// 3. 同步更新JSON配置文件
///
/// 使用方法:
/// - 自动在ArtTableSO的OnValidate或保存时调用FillResourcePaths
/// - 手动:菜单 -> 美术工具 -> 更新所有美术资源路径
/// </summary>
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";
/// <summary>
/// 自动填充单个SO的资源路径
/// 应该在ArtTableSO保存时调用
/// </summary>
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
}
}
/// <summary>
/// 同步SO数据到JSON文件
/// </summary>
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}");
}
}
/// <summary>
/// 生成合并的Thrift Bytes文件调用ArtResourceConfigEditor的方法
/// </summary>
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}");
}
}
/// <summary>
/// 手动批量更新所有ArtTableSO的资源路径
/// </summary>
[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<ArtTableSO>(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模式下正常加载资源了",
"确定"
);
}
/// <summary>
/// 验证所有SO的路径完整性
/// </summary>
[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<ArtTableSO>(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建议运行【美术工具 -> 更新所有美术资源路径】修复",
"确定"
);
}
}
}
/// <summary>
/// ArtTableSO的自定义Inspector
/// 在Inspector底部添加"更新路径"按钮
/// </summary>
[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
);
}
}
/// <summary>
/// 资源保存时自动填充路径
/// </summary>
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<ArtTableSO>(assetPath);
if (table != null)
{
ArtResourcePathFiller.FillResourcePaths(table);
}
}
}
}
/// <summary>
/// JSON数据结构
/// </summary>
[System.Serializable]
public class ArtTableJsonData
{
public int TableId;
public string TableName;
public List<ArtItemJsonData> 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