There are four main functional modules:
- Find References: View all resources referenced by a given Prefab
- Find Referenced Parties: View all Prefabs that reference a given resource
- Reference relationship: Browse all Prefab reference relationships
- Referenced Relationships: Browse referenced relationships for all resources
Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Linq;
public class AssetDependenciesTool : EditorWindow
{
//Prefab/Scene的引用资源
private Dictionary<string,string[]> prefabDependDic = new Dictionary<string,string[]>();
//被Prefab/Scene引用的资源
private Dictionary<string,List<string>> assetRelatedDic = new Dictionary<string,List<string>>();
//所有引用资源类型
private string[] assetExtentArray = new string[0];
private List<AssetRelatedItem> showAssetItemList = new List<AssetRelatedItem>();
private List<PrefabDependItem> showPrefabItemList;
private int displayIndex = 0;
private int lastExtentIndex = -1;
private int showExtentIndex = -1;
private bool showUsedAsset = true;
private bool showUnusedAsset = true;
private Color listItemColor = new Color(0.9f,0.9f,0.9f,1);
[MenuItem("MyTools/查找资源引用关系"]
public static void FindAssets()
{
GetWindow<AssetDependenciesTool>();
}
private void OnEnable()
{
lastExtentIndex = -1;
showExtentIndex = -1;
showUsedAsset = true;
showUnusedAsset = true;
FindAllPrefabDependencies(ref prefabDependDic,ref assetRelatedDic,out assetExtentArray);
}
private string[] displayBarArray = new string[] { "查找引用方","查找被引用方","引用关系","被引用关系" };
private void OnGUI()
{
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField("有引用资源的Prefab&Scene数量 : " + prefabDependDic.Count);
EditorGUILayout.LabelField("被Prefab|Scene引用的资源数量 : " + assetRelatedDic.Count);
EditorGUILayout.LabelField("所统计的资源引用关系,不包含代码动态加载");
EditorGUILayout.EndVertical();
displayIndex = GUILayout.Toolbar(displayIndex,displayBarArray);
switch(displayIndex)
{
case 0:
OnGUI_FindPrefab();
break;
case 1:
OnGUI_FindAssets();
break;
case 2:
OnGUI_AllPrefab();
break;
case 3:
OnGUI_AllAssets();
break;
default:
break;
}
}
Object selectPrefabObject;
PrefabDependItem selectPrefabDependItem;
private void OnGUI_FindPrefab()
{
EditorGUILayout.BeginHorizontal();
selectPrefabObject = EditorGUILayout.ObjectField("引用资源:",selectPrefabObject,typeof(GameObject),true,GUILayout.Width(400));
if(GUILayout.Button("引用详情",GUILayout.Width(100)))
{
if(selectPrefabObject != null)
{
string prefabPath = AssetDatabase.GetAssetPath(selectPrefabObject);
string[] depPathArray;
if(!prefabDependDic.TryGetValue(prefabPath,out depPathArray))
depPathArray = new string[0];
selectPrefabDependItem = new PrefabDependItem(prefabPath,depPathArray);
selectPrefabDependItem.LoadDependObjects();
}
}
EditorGUILayout.EndHorizontal();
if(selectPrefabDependItem != null && selectPrefabDependItem.dependPathArray != null)
{
EditorGUILayout.Space(5);
EditorGUILayout.BeginVertical("box");
for(int i = 0; i < selectPrefabDependItem.dependObjectArray.Length; i++)
{
EditorGUILayout.ObjectField($"({i}) 引用资源:",selectPrefabDependItem.dependObjectArray[i],typeof(Object),true,GUILayout.Width(400));
}
EditorGUILayout.EndVertical();
}
}
Object selectAssetObject;
AssetRelatedItem selectAssetRelatedItem;
private void OnGUI_FindAssets()
{
EditorGUILayout.BeginHorizontal();
selectAssetObject = EditorGUILayout.ObjectField("被引用资源:",selectAssetObject,typeof(Object),true,GUILayout.Width(400));
if(GUILayout.Button("引用详情",GUILayout.Width(100)))
{
if(selectAssetObject != null)
{
string prefabPath = AssetDatabase.GetAssetPath(selectAssetObject);
string[] depPathArray;
if(!prefabDependDic.TryGetValue(prefabPath,out depPathArray))
depPathArray = new string[0];
selectAssetRelatedItem = new AssetRelatedItem(prefabPath,depPathArray.ToList());
selectAssetRelatedItem.LoadRelatedObjects();
}
}
EditorGUILayout.EndHorizontal();
if(selectAssetRelatedItem != null && selectAssetRelatedItem.relatedObjectList != null)
{
EditorGUILayout.Space(5);
EditorGUILayout.BeginVertical("box");
for(int i = 0; i < selectAssetRelatedItem.relatedObjectList.Count; i++)
{
EditorGUILayout.ObjectField($"({i}) 引用资源:",selectAssetRelatedItem.relatedObjectList[i],typeof(GameObject),true,GUILayout.Width(400));
}
EditorGUILayout.EndVertical();
}
}
int showPageIndex = 0;
Vector2 scrollViewPos03;
private void OnGUI_AllPrefab()
{
if(showPrefabItemList == null)
{
FindAllPrefabDepend(prefabDependDic,out showPrefabItemList);
}
EditorGUILayout.LabelField($"有引用的资源数量: { showPrefabItemList.Count}");
scrollViewPos03 = EditorGUILayout.BeginScrollView(scrollViewPos03);
for(int i = showPageIndex; i < showPageIndex + 100; i++)
{
if(i >= showPrefabItemList.Count)
break;
PrefabDependItem prefabDependItem = showPrefabItemList[i];
GUI.color = i % 2 == 0 ? listItemColor : Color.white;
EditorGUILayout.BeginVertical("box");
GUI.color = Color.white;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.ObjectField($"{i}. 资源文件: ",prefabDependItem.prefabObject,typeof(GameObject),true,GUILayout.Width(400));
EditorGUILayout.LabelField($"引用数量: { prefabDependItem.dependPathArray.Length}",GUILayout.Width(200));
if(GUILayout.Button("引用详情",GUILayout.Width(100)))
{
if(prefabDependItem.dependObjectArray == null)
prefabDependItem.LoadDependObjects();
else
prefabDependItem.dependObjectArray = null;
}
EditorGUILayout.EndHorizontal();
if(prefabDependItem.dependObjectArray != null)
{
EditorGUILayout.Space(5);
EditorGUILayout.BeginVertical();
for(int j = 0; j < prefabDependItem.dependObjectArray.Length; j++)
{
EditorGUILayout.ObjectField($"({j}) 引用资源:",prefabDependItem.dependObjectArray[j],typeof(Object),true,GUILayout.Width(400));
}
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndScrollView();
EditorGUILayout.BeginHorizontal();
if(GUILayout.Button("上一页",GUILayout.Width(200)))
{
if(showPageIndex >= 100)
showPageIndex -= 100;
}
EditorGUILayout.LabelField($"{showPageIndex}/{showPrefabItemList.Count}");
if(GUILayout.Button("下一页",GUILayout.Width(200)))
{
if((showPageIndex + 100) < showPrefabItemList.Count)
showPageIndex += 100;
}
EditorGUILayout.EndHorizontal();
}
Vector2 scrollViewPos04;
string selectAssetExtent;
private void OnGUI_AllAssets()
{
showExtentIndex = EditorGUILayout.Popup("查找资源类型 : ",showExtentIndex,assetExtentArray,GUILayout.MaxWidth(300));
if(showExtentIndex < 0 || showExtentIndex >= assetExtentArray.Length)
return;
selectAssetExtent = assetExtentArray[showExtentIndex];
if(lastExtentIndex != showExtentIndex)
{
lastExtentIndex = showExtentIndex;
FindAssetsByExtent(selectAssetExtent,assetRelatedDic,ref showAssetItemList);
}
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField($"被引用资源的数量: { showAssetItemList.Count}");
showUsedAsset = EditorGUILayout.Toggle("显示被使用资源",showUsedAsset);
showUnusedAsset = EditorGUILayout.Toggle("显示未使用资源",showUnusedAsset);
EditorGUILayout.EndVertical();
scrollViewPos04 = EditorGUILayout.BeginScrollView(scrollViewPos04);
for(int i = 0; i < showAssetItemList.Count; i++)
{
AssetRelatedItem assetRelatedItem = showAssetItemList[i];
if((!showUsedAsset && assetRelatedItem.relatedPathList.Count > 0) || (!showUnusedAsset && assetRelatedItem.relatedPathList.Count == 0))
continue;
GUI.color = i % 2 == 0 ? listItemColor : Color.white;
EditorGUILayout.BeginVertical("box");
GUI.color = Color.white;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.ObjectField($"{i}. 资源文件: ",assetRelatedItem.assetObject,typeof(GameObject),true,GUILayout.Width(400));
EditorGUILayout.LabelField($"被引用次数: { assetRelatedItem.relatedPathList.Count}",GUILayout.Width(200));
if(assetRelatedItem.relatedPathList.Count > 0)
{
if(GUILayout.Button("引用详情",GUILayout.Width(100)))
{
if(assetRelatedItem.relatedObjectList == null)
assetRelatedItem.LoadRelatedObjects();
else
assetRelatedItem.relatedObjectList = null;
}
}
EditorGUILayout.EndHorizontal();
if(assetRelatedItem.relatedObjectList != null)
{
EditorGUILayout.Space(5);
EditorGUILayout.BeginVertical();
for(int j = 0; j < assetRelatedItem.relatedObjectList.Count; j++)
{
EditorGUILayout.ObjectField($"({j}) 引用方:",assetRelatedItem.relatedObjectList[j],typeof(GameObject),true,GUILayout.Width(400));
}
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndScrollView();
}
//==========
private void FindAllPrefabDependencies(ref Dictionary<string,string[]> prefabDic,ref Dictionary<string,List<string>> assetDic,out string[] extentArray)
{
prefabDic.Clear();
assetDic.Clear();
extentArray = new string[0];
string[] allFilePathArray = Directory.GetFiles(Application.dataPath,"*.*",SearchOption.AllDirectories);
EditorUtility.DisplayProgressBar("读取项目所有资源","读取所有资源",0);
for(int i = 0; i < allFilePathArray.Length; i++)
{
if(allFilePathArray[i].EndsWith(".meta"))
allFilePathArray[i] = null;
else
allFilePathArray[i] = "Assets" + allFilePathArray[i].Replace(Application.dataPath,"");
}
EditorUtility.ClearProgressBar();
//----------
EditorUtility.DisplayProgressBar("获取资源引用关系","遍历所有资源",0);
for(int i = 0; i < allFilePathArray.Length; i++)
{
if(string.IsNullOrEmpty(allFilePathArray[i]))
continue;
if(allFilePathArray[i].EndsWith(".prefab") || allFilePathArray[i].EndsWith(".unity"))
{
if(i % 100 == 0 && EditorUtility.DisplayCancelableProgressBar("获取资源引用关系",$"遍历所有文件 : {i}/{allFilePathArray.Length}",(float)i / allFilePathArray.Length))
break;
prefabDic[allFilePathArray[i]] = AssetDatabase.GetDependencies(allFilePathArray[i],true);
}
}
EditorUtility.ClearProgressBar();
//----------
EditorUtility.DisplayProgressBar("反向解析引用关系","遍历所有资源",0);
for(int i = 0; i < allFilePathArray.Length; i++)
{
if(string.IsNullOrEmpty(allFilePathArray[i]))
continue;
if(i % 100 == 0 && EditorUtility.DisplayCancelableProgressBar("反向解析引用关系",$"遍历所有文件 : {i}/{allFilePathArray.Length}",(float)i / allFilePathArray.Length))
break;
if(!assetDic.TryGetValue(allFilePathArray[i],out var relList))
{
relList = assetDic[allFilePathArray[i]] = new List<string>();
}
foreach(var item in prefabDic)
{
if(item.Value.Contains(allFilePathArray[i]))
{
relList.Add(item.Key);
}
}
}
EditorUtility.ClearProgressBar();
//----------
EditorUtility.DisplayProgressBar("读取引用资源类型","获取所有引用文件类型",0);
string extentName;
List<string> extensionList = new List<string>();
foreach(var item in assetDic.Keys)
{
extentName = Path.GetExtension(item);
if(!extensionList.Contains(extentName))
{
extensionList.Add(extentName);
}
}
extentArray = extensionList.ToArray();
EditorUtility.ClearProgressBar();
}
private void FindAssetsByExtent(string targetExtent,Dictionary<string,List<string>> assetRelDic,ref List<AssetRelatedItem> assetItemList)
{
assetItemList.Clear();
if(string.IsNullOrEmpty(targetExtent))
return;
int index = 0;
foreach(var item in assetRelDic)
{
if(item.Key.EndsWith(targetExtent))
{
if(EditorUtility.DisplayCancelableProgressBar("查找指定类型的资源引用",$"遍历所有引用 : {assetItemList.Count}",(float)(++index) / assetRelDic.Count))
{
EditorUtility.ClearProgressBar();
return;
}
assetItemList.Add(new AssetRelatedItem(item.Key,item.Value));
}
}
EditorUtility.ClearProgressBar();
}
private void FindAllPrefabDepend(Dictionary<string,string[]> prefabDepDic,out List<PrefabDependItem> prefabItemList)
{
prefabItemList = new List<PrefabDependItem>();
int index = 0;
foreach(var item in prefabDepDic)
{
if(EditorUtility.DisplayCancelableProgressBar("查找所有资源的引用关系",$"加载所有资源文件: {prefabItemList.Count}",(float)(++index) / prefabDepDic.Count))
{
EditorUtility.ClearProgressBar();
return;
}
prefabItemList.Add(new PrefabDependItem(item.Key,item.Value));
}
EditorUtility.ClearProgressBar();
}
//==========
public class AssetRelatedItem
{
public string assetPath;
public List<string> relatedPathList;
public Object assetObject;
public List<Object> relatedObjectList;
public AssetRelatedItem(string _assetPath,List<string> _relatedPathList)
{
assetPath = _assetPath;
relatedPathList = _relatedPathList;
assetObject = AssetDatabase.LoadAssetAtPath<Object>(assetPath);
}
public void LoadRelatedObjects()
{
if(relatedObjectList != null)
return;
if(relatedPathList == null || relatedPathList.Count == 0)
return;
relatedObjectList = new List<Object>(relatedPathList.Count);
for(int i = 0; i < relatedPathList.Count; i++)
{
if(EditorUtility.DisplayCancelableProgressBar("加载引用该资源的Prefab|Scene",$"加载进度 : {i}/{relatedPathList.Count}",(float)i / relatedPathList.Count))
{
EditorUtility.ClearProgressBar();
return;
}
Object tempObj = AssetDatabase.LoadAssetAtPath<Object>(relatedPathList[i]);
relatedObjectList.Add(tempObj);
}
EditorUtility.ClearProgressBar();
}
}
public class PrefabDependItem
{
public string prefabPath;
public string[] dependPathArray;
public Object prefabObject;
public Object[] dependObjectArray;
public PrefabDependItem(string _prefabPath,string[] _dependPathArray)
{
prefabPath = _prefabPath;
dependPathArray = _dependPathArray;
prefabObject = AssetDatabase.LoadAssetAtPath<Object>(prefabPath);
}
public void LoadDependObjects()
{
if(dependObjectArray != null)
return;
if(dependPathArray == null || dependPathArray.Length == 0)
return;
dependObjectArray = new Object[dependPathArray.Length];
for(int i = 0; i < dependPathArray.Length; i++)
{
if(EditorUtility.DisplayCancelableProgressBar("加载引用该资源的Prefab|Scene",$"加载进度 : {i}/{dependPathArray.Length}",(float)i / dependPathArray.Length))
{
EditorUtility.ClearProgressBar();
return;
}
Object tempObj = AssetDatabase.LoadAssetAtPath<Object>(dependPathArray[i]);
dependObjectArray[i] = tempObj;
}
EditorUtility.ClearProgressBar();
}
}
}