Unity资源反向引用查询

Unity编辑器   2023-04-14 09:43   1030   0  

前言

Unity的资源反向引用查询,指的是在Unity编辑器中查找指定资源被哪些其他资源引用。由于Unity默认不支持反向查询,所以需要使用代码实现。

具体实现方法是通过遍历所有的资源文件,获取每个资源文件的依赖关系,并将依赖关系记录到一个配置文件中。然后在查询时,读取这个配置文件,根据目标资源查找其被哪些其他资源引用。

相比正向查询,反向查询需要更多的计算和存储资源依赖信息,因此比较耗费性能和空间。但是,反向查询对于资源的优化、重构和清理非常有用,可以帮助开发者快速找到哪些资源没有被引用,进而进行删除或者其他操作。


实现一个查找资源引用关系的工具。

使用介绍:

菜单项 "Assets/20.查找选中 -> 引用关系" 实现了查找选中资源的依赖关系,并在控制台输出结果。

菜单项 "Assets/21.查找选中 -> 反向引用关系" 实现了查找选中资源的反向引用关系,并在控制台输出结果。

菜单项 "Assets/22.查找选中 -> 反向引用关系(先刷新后查找,无修改资源执行一次后用21即可)" 实现了执行反向引用关系查找前,先刷新资源依赖信息。

菜单项 "GM/导出所有资源的反向引用" 实现了将所有资源的反向引用关系导出为CSV文件,并在输出文件位置打开该CSV文件。

菜单项 "GM/导出所有没有引用的 Texture、Sprite、Shader" 实现了将所有没有其他资源引用的Texture、Sprite、Shader导出为CSV文件,并在输出文件位置打开该CSV文件。

菜单项 "GM/刷新 扫描 Prefab、Material、Scene 的引用 config" 实现了刷新Prefab、Material、Scene的引用关系信息。

菜单项 "GM/刷新 扫描 Texture、Sprite 的引用 config" 实现了刷新Texture、Sprite的引用关系信息。


asset文件生成,这个文件保存所有资源的引用关系

/*----------------------------------------------------------------
 Created by 王银
 文件名: DependenciesConfig
 创建时间: 2022/10/21
----------------------------------------------------------------*/
using System.Collections.Generic;
using System.IO;
using Sirenix.OdinInspector;
using UnityEditor;
using UnityEngine;


[CreateAssetMenu(fileName = "DependenciesConfigs", menuName = "CreatConfig/DependenciesConfig")]
public class DependenciesConfig : SerializedScriptableObject
{
	private static DependenciesConfig _inst;
	//单例
	public static DependenciesConfig Inst
	{
		get
		{
			if (_inst == null)
			{
				_inst = LoadAndCreatAssets();
				EditorUtility.SetDirty(_inst);
			}
			return _inst;
		}
	}
	//加载或者创建Asset
	private static DependenciesConfig LoadAndCreatAssets()
	{
		DependenciesConfig asset = GetDependenciesConfig();
		if (asset == null)
		{
			string _assetPath = "Assets/Debug/WangYin/Test/Editor/Config/DependenciesConfigs.asset";
			if (File.Exists(_assetPath))
				File.Delete(_assetPath);
			asset = CreateInstance<DependenciesConfig>();
			string directory = Path.GetDirectoryName(_assetPath);
			if (!Directory.Exists(directory))
			{
				Directory.CreateDirectory(directory);
			}
			AssetDatabase.CreateAsset(asset, _assetPath);
			AssetDatabase.SaveAssets();
		}
		return asset;
	}

	//依赖列表
	[LabelText("资源依赖暂存表")]
	[ShowInInspector]
	[ReadOnly]
	[DictionaryDrawerSettings(IsReadOnly = true, KeyLabel = "资源Path : key ", ValueLabel = "Key的反向引用列表"), PropertyOrder(1)]
	public Dictionary<string, List<string>> dDependencies = new Dictionary<string, List<string>>();

	[LabelText("无依赖的资源 暂存表")]
	[ShowInInspector]
	[ListDrawerSettings(IsReadOnly = true), PropertyOrder(3)]
	public List<string> noDependenciesList = new List<string>();

	public Dictionary<string, List<string>> GetDependencies()
	{
		return dDependencies;
	}
	public List<string> GetNoDependenciesList()
	{
		return noDependenciesList;
	}

	[Button(ButtonSizes.Large), LabelText("点击扫描Prefab、Material、Scene"), GUIColor(0.6f, 0.1f, 0.6f), PropertyOrder(-1)]
	public void FindReverseDependencies()
	{
		dDependencies.Clear();
		var sw = new System.Diagnostics.Stopwatch();
		sw.Start();
		string[] guids = AssetDatabase.FindAssets("t:Prefab t:Material t:Scene");
		for (int i = 0; i < guids.Length; i++)
		{
			string path = AssetDatabase.GUIDToAssetPath(guids[i]);
			if (EditorUtility.DisplayCancelableProgressBar($"扫描中{i + 1}/{guids.Length}", path, (float)(i + 1) / guids.Length))
			{
				EditorUtility.ClearProgressBar();
				return;
			}
			foreach (var dependenciePath in AssetDatabase.GetDependencies(path, false))
			{

				if (dDependencies.TryGetValue(dependenciePath, out List<string> list))
				{
					if (!list.Contains(path))
						list.Add(path);
				}
				else
				{
					dDependencies.Add(dependenciePath, new List<string>() { path });
				}
			}
		}

		EditorUtility.ClearProgressBar();
		Debug.Log("扫描结束");

		sw.Stop();
		//  获取当前实例测量得出的总时间
		double seconds = sw.Elapsed.TotalSeconds;
		//打印代码执行时间
		Debug.Log($"整个流程耗时:{seconds} 秒。");

		EditorUtility.SetDirty(Inst);
		AssetDatabase.SaveAssets();
		AssetDatabase.Refresh();
	}
	public static DependenciesConfig GetDependenciesConfig()
	{
		var path = "Assets/Debug/WangYin/Test/Editor/Config/DependenciesConfigs.asset";
		return GetDataByAssetPath(path) as DependenciesConfig;
	}
	public static Object GetDataByAssetPath(string assetPath)
	{
		return AssetDatabase.LoadAssetAtPath<Object>(assetPath);
	}

	[Button(ButtonSizes.Large), LabelText("点击扫描 Texture、Sprite、Shader"), GUIColor(0.3f, 0.8f, 0.3f), PropertyOrder(2)]
	public void FindNoReverseDependencies()
	{
		noDependenciesList.Clear();
		var sw = new System.Diagnostics.Stopwatch();
		sw.Start();
		string[] guids = AssetDatabase.FindAssets("t:Texture t:Sprite t:Shader");
		for (int i = 0; i < guids.Length; i++)
		{
			string path = AssetDatabase.GUIDToAssetPath(guids[i]);
			if (EditorUtility.DisplayCancelableProgressBar($"扫描中{i + 1}/{guids.Length}", path, (float)(i + 1) / guids.Length))
			{
				EditorUtility.ClearProgressBar();
				return;
			}
			if (!dDependencies.TryGetValue(path, out List<string> list) || list.Count == 0)
			{
				if (!noDependenciesList.Contains(path))
					noDependenciesList.Add(path);
			}
		}

		EditorUtility.ClearProgressBar();
		Debug.Log("扫描结束");

		sw.Stop();
		//  获取当前实例测量得出的总时间
		double seconds = sw.Elapsed.TotalSeconds;
		//打印代码执行时间
		Debug.Log($"整个流程耗时:{seconds} 秒。");

		EditorUtility.SetDirty(Inst);
		AssetDatabase.SaveAssets();
		AssetDatabase.Refresh();
	}
}

查询工具

/*----------------------------------------------------------------
 Created by 王银
 文件名: DependenciesConfig
 创建时间: 2022/10/21
----------------------------------------------------------------*/
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;

public static class ReferenceFinder
{

    [MenuItem("Assets/20.查找选中 -> 引用关系", false, 20)]
    public static void FindDependencies()
    {
        foreach (var guid in Selection.assetGUIDs)
        {
            string path = AssetDatabase.GUIDToAssetPath(guid);
            foreach (var dependenciePath in AssetDatabase.GetDependencies(path, true))
            {
                if (path != dependenciePath && !dependenciePath.Contains("cs"))
                    Debug.Log(string.Format("<color=red>{0}</color>   依赖  ---> <color=cyan> {1} </color>", path, dependenciePath));
            }
        }
        Debug.Log("查找依赖结束");
    }

    [MenuItem("Assets/21.查找选中 -> 反向引用关系", false, 21)]
    public static void FindReverseDependencies()
    {
        List<string> asserts = new List<string>();

        foreach (var guid in Selection.assetGUIDs)
        {
            asserts.Add(AssetDatabase.GUIDToAssetPath(guid));
        }

        Dictionary<string, List<string>> dDependencies = DependenciesConfig.Inst.GetDependencies();
        foreach (var assert in asserts)
        {
            if (dDependencies.TryGetValue(assert, out List<string> list))
            {
                if (list != null && list.Count > 0)
                {
                    for (int i = 0; i < list.Count; i++)
                    {
                        if (i == 0)
                        {
                            Debug.Log(string.Format("<color=red>{0} 的反向引用如下  --- v </color>", assert));
                        }
                        Debug.Log(string.Format("<color=cyan> \t{0}.\t{1} </color>", i + 1, list[i]));
                    }
                }
            }
        }
        EditorUtility.ClearProgressBar();
        Debug.Log("查找依赖结束");
    }

    [MenuItem("Assets/22.查找选中 -> 反向引用关系(先刷新后查找,无修改资源执行一次后用21即可)", false, 22)]
    public static void RefreshFindReverseDependencies()
    {
        DependenciesConfig.Inst.FindReverseDependencies();
        FindReverseDependencies();
    }

    [MenuItem("GM/导出所有资源的反向引用", priority = 20)]
    public static void PrintReverseDependencies()
    {
        var sw = new System.Diagnostics.Stopwatch();
        sw.Start();
        StringBuilder outPut = new StringBuilder();
        outPut.AppendLine("被引用的");
        List<string> pathList = new List<string>();
        int count = 0;
        List<string> keys = new List<string>()
        {
            "Assets/Art/Common",
            "Packages/com.unity.render-pipelines.universal/Editor/AssetVersion.cs",
            "Packages/com.unity.render-pipelines.universal/Shaders/Lit.shader",
            "Packages/com.unity.render-pipelines.universal/Shaders/Unlit.shader",
			//".cs",
			//"Packages/com.unity",
			//".overrideController",
			//".fbx",
			//".controller",
			//".asset"
		};
        bool isContinue(string key)
        {
            for (int i = 0; i < keys.Count; i++)
            {
                if (key.Contains(keys[i]))
                {
                    return true;
                }
            }
            return false;
        }

        Dictionary<string, List<string>> dDependencies = DependenciesConfig.Inst.GetDependencies();
        foreach (var item in dDependencies)
        {
            if (isContinue(item.Key))
            {
                continue;
            }
            pathList = item.Value;
            count = pathList.Count;
            for (int i = 0; i < count; i++)
            {
                string key = i == 0 ? item.Key : "";
                outPut.AppendLine($"{key},{pathList[i]}");
            }
        }
        string textFolderPath = Path.Combine(Application.dataPath, "..", "Profiling", "FindDependencies");

        Directory.CreateDirectory(textFolderPath);
        string outputFileName = Path.Combine(textFolderPath,
            string.Format("FindDependencies{0}.csv", System.DateTime.Now.ToString("yyyyMMddHHmmss")));
        File.WriteAllText(outputFileName, outPut.ToString(), Encoding.UTF8);
        Application.OpenURL(outputFileName);
        sw.Stop();
        //  获取当前实例测量得出的总时间
        double seconds = sw.Elapsed.TotalMilliseconds;
        //打印代码执行时间
        Debug.Log($"整个流程耗时:{seconds} 毫秒。");
    }

    [MenuItem("GM/导出所有没有引用的 Texture、Sprite、Shader ", priority = 21)]
    public static void PrintNotDependenciesAsset()
    {
        var sw = new System.Diagnostics.Stopwatch();
        sw.Start();
        StringBuilder outPut = new StringBuilder();
        outPut.AppendLine("没有引用的:");

        List<string> keys = new List<string>()
        {
            "Packages/com",
        };
        bool isContinue(string key)
        {
            for (int i = 0; i < keys.Count; i++)
            {
                if (key.Contains(keys[i]))
                {
                    return true;
                }
            }
            return false;
        }
        List<string> list = DependenciesConfig.Inst.GetNoDependenciesList();
        foreach (var item in list)
        {
            if (isContinue(item))
            {
                continue;
            }
            Object obj = AssetDatabase.LoadAssetAtPath<Object>(item);
            System.Type type = obj.GetType();
            outPut.AppendLine($",{type},{item}");
            Resources.UnloadAsset(obj);
        }

        string textFolderPath = Path.Combine(Application.dataPath, "..", "Profiling", "FindDependencies");
        Directory.CreateDirectory(textFolderPath);
        string outputFileName = Path.Combine(textFolderPath,
            string.Format("NotDependencieAsset{0}.csv", System.DateTime.Now.ToString("yyyyMMddHHmmss")));
        File.WriteAllText(outputFileName, outPut.ToString(), Encoding.UTF8);
        sw.Stop();
        //  获取当前实例测量得出的总时间
        double seconds = sw.Elapsed.TotalMilliseconds;
        //打印代码执行时间
        Debug.Log($"整个流程耗时:{seconds} 毫秒。");
        Application.OpenURL(outputFileName);
        System.GC.Collect();
    }

    [MenuItem("GM/刷新 扫描 Prefab、Material、Scene 的引用 config", priority = 0)]
    public static void ScanningAll()
    {
        DependenciesConfig.Inst.FindReverseDependencies();
    }
    [MenuItem("GM/刷新 扫描 Texture、Sprite 的引用  config", priority = 1)]
    public static void FindNoReverseDependencies()
    {
        DependenciesConfig.Inst.FindNoReverseDependencies();
    }
}

博客评论
还没有人评论,赶紧抢个沙发~
发表评论
说明:请文明发言,共建和谐网络,您的个人信息不会被公开显示。