查找资源引用参考

EditorUtility.DisplayCancelableProgressBar无法正常取消的问题

清空Unity控制台Log

用到的API:

  • Directory.GetFiles() :返回符合指定条件的文件名链接

  • Path.GetExtension() :返回指定路径的扩展名链接

  • EditorUtility.DisplayCancelableProgressBar() :显示或更新含有 Cancel 按钮的进度条。链接

  • AssetDatabase.GetAssetPath() : 获取对象的路径; AssetDatabase.AssetPathToGUID() :通过路径获取对应的GUID ;AssetDatabase.LoadAssetAtPath() : 通过路径定位到资源位置。链接

额外扩展:

Unity 检查丢失引用的资源 Missing

Unity 资源引用 - 狂飙

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;

public static class FindUIReferences
{

    private static MethodInfo clearConsole;
    [MenuItem("Assets/Find UI References", false, 10)]
    private static void Find()
    {
        EditorSettings.serializationMode = SerializationMode.ForceText;
        var    objects   = Selection.objects;//获取所有选中的对象。
        
        ClearConsole();//清除控制台中已有的Log日志。
        var withoutExtensions = new List<string>() { ".prefab", ".unity", ".mat", ".asset" };// 所查找文件的后缀。

        List<string> files = Directory.GetFiles(Application.dataPath + "/Bundles/UI", "*.*", SearchOption.AllDirectories).Where
                (s => withoutExtensions.Contains(Path.GetExtension(s).ToLower())).ToList();

        files.AddRange
        (
            Directory.GetFiles(Application.dataPath + "/ArtsUI/UI", "*.*", SearchOption.AllDirectories).Where
                    (s => string.Equals(Path.GetExtension(s).ToLower(), ".spriteatlas")).ToList()
        ); // 所查找文件的路径。

        bool                    clickCancelBtn     = false;
        bool                    cancel             = false;//添加取消查找标志位,防止手动取消时无法取消 Ps:取消时没有正确退出While循环,导致update仍然继续运行。
        int                     objectIndex        = 0;
        Dictionary<Object, int> objectFindCountDic = new Dictionary<Object, int>(); 
        string                  redMessage       = "<color=#FF0000>{0}</color>";

        EditorApplication.update = delegate()//update回调,每帧执行查找操作。
        {
            while (objectIndex < objects.Length)
            {
                var    tempObject = objects[objectIndex];
                string path       = AssetDatabase.GetAssetPath(tempObject);//通过对象获取路径;用于查找对象的GUID
                int    startIndex = 0;
                if (!string.IsNullOrEmpty(path))
                {
                    objectFindCountDic[tempObject] = 0;
                    string guid = AssetDatabase.AssetPathToGUID(path);//通过路径获取对应的GUID

                    while (startIndex < files.Count)
                    {
                        string file = files[startIndex];

                        clickCancelBtn = EditorUtility.DisplayCancelableProgressBar("匹配资源中", file, (float)startIndex / (float)files.Count);//创建带有取消功能的进度条。返回值为是否关闭进度条。

                        if (Regex.IsMatch(File.ReadAllText(file), guid))//正则表达式匹配
                        {
                            Debug.Log($"资源名称: {tempObject.name} ; 引用路径: {file}", AssetDatabase.LoadAssetAtPath<Object>(GetRelativeAssetsPath(file)));
                            objectFindCountDic[tempObject]++;
                        }

                        if (clickCancelBtn)
                        {
                            cancel = true;

                            break;
                        }

                        startIndex++;
                    }
                }

                objectIndex++;

                if (cancel || objectIndex >= objects.Length)
                {
                    EditorUtility.ClearProgressBar();//关闭进度条
                    EditorApplication.update = null;//update回调置空
                    startIndex               = 0;

                    Debug.Log($"{(cancel? string.Format(redMessage, "手动取消匹配,匹配结束。") : "文件查找完成,匹配结束。")}");

                    foreach (KeyValuePair<Object, int> keyValuePair in objectFindCountDic)
                    {
                        string assetPath = AssetDatabase.GetAssetPath(keyValuePair.Key);
                        Debug.Log
                        (
                            $"资源名称:{keyValuePair.Key.name + Path.GetExtension(assetPath)};所匹配资源个数:{(keyValuePair.Value <= 0? string.Format(redMessage, keyValuePair.Value) : keyValuePair.Value)}",
                            AssetDatabase.LoadAssetAtPath<Object>(assetPath)
                        );// 添加:AssetDatabase.LoadAssetAtPath<Object>(assetPath) 使添加log时可以定位到对应的资源。
                    }

                    break;
                }
            }
        };
    }

    private static void ClearConsole()
    {
        if (clearConsole == null)
        {
            //获取 UnityEditor.LogEntries 程序集中的Clear方法并调用。
            var log = typeof (EditorWindow).Assembly.GetType("UnityEditor.LogEntries");
            clearConsole = log.GetMethod("Clear");
        }

        clearConsole?.Invoke(null, null);
    }
    [MenuItem("Assets/Find References", true)]
    private static bool VFind()
    {
        string path = AssetDatabase.GetAssetPath(Selection.activeObject);
        return (!string.IsNullOrEmpty(path));
    }

    private static string GetRelativeAssetsPath(string path)
    {
        return "Assets" + Path.GetFullPath(path).Replace(Path.GetFullPath(Application.dataPath), "").Replace('\\', '/');
    }
}