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的引用关系信息。
/*---------------------------------------------------------------- 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(); } }