FishPlayer

一个喜欢摸鱼的废物

0%

查询Unity Object在工程中的引用

前言

上周把bug修得差不多了,这周稍微有点空闲时间了,就把以前想做的一些东西大概看了一下。
之前因为清理文件没有及时清除相关引用,结果项目跑起来的时候有各种沙雕报错。只能再一个个物体检查,好麻烦。
于是想试试能不能查到某个物体被其它哪些物体引用。

思路

好在 Unity提供了这个函数 AssetDatabase.GetDependencies() ,可以获得某个 asset 的依赖项。
AssetDatabase.GetDependencies()

假设我们需要知道物体 A 被哪些其他的物体引用。那就遍历所有物体的依赖项,把依赖项中包含物体 A 的物体筛选出来即可。

上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

// to find the reference of the target unity object

public static class FindReferencesInProject
{
public static bool TryFindReferenceOfUobject(UnityEngine.Object uobject)
{
bool result = false;
string path = AssetDatabase.GetAssetPath(uobject);
if (AssetDatabase.IsValidFolder(path))
{
// dun do it for a folder :)
return false;
}

FindObjectReference(uobject);

return result;
}

// why the order is like this?
[MenuItem("Assets/Find References In Project")]
public static void FindSelectedObjectReferences()
{
if (null != Selection.activeObject)
{
string path = AssetDatabase.GetAssetPath(Selection.activeObject);
if (!AssetDatabase.IsValidFolder(path))
{
FindObjectReference(Selection.activeObject);
}
}
}

private static void FindObjectReference(UnityEngine.Object uobject)
{
System.Diagnostics.Stopwatch stopWatcher = new System.Diagnostics.Stopwatch();
stopWatcher.Start();

string targetPath = AssetDatabase.GetAssetPath(uobject);
List<string> referencesPaths = new List<string>();
Dictionary<string, List<string>> referenceCache = new Dictionary<string, List<string>>();
string[] assetGuids = AssetDatabase.FindAssets("");

for (int i = 0; i < assetGuids.Length; i++)
{
string assetPath = AssetDatabase.GUIDToAssetPath(assetGuids[i]);
string[] dependencies = AssetDatabase.GetDependencies(assetPath, false);

for (int j = 0; j < dependencies.Length; j++)
{
if (dependencies[j] == targetPath)
{
referencesPaths.Add(assetPath);
}
}
}

FindReferencesInProjectEditor.OpenWindow();
FindReferencesInProjectEditor window = FindReferencesInProjectEditor.GetWindow();
window.SetPaths(referencesPaths.ToArray());
window.SetTargetUobject(uobject);

stopWatcher.Stop();
Debug.Log($"search references for {uobject.name} cost {stopWatcher.ElapsedMilliseconds}ms");

referenceCache.Clear();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

// a simple window to show the result, and also we can ping those result object :D

public class FindReferencesInProjectEditor : EditorWindow
{
private Rect m_viewportRect = default;
private Rect m_listRect = default;
private Rect m_bottomRect = default;

private float m_scrollPosition = default;
private string[] m_targetPaths = null;

private const float ELEMENT_HEIGHT = 20.0f;
private const float ELEMENT_ICON_SIZE = 20.0f;
private const float SCROLLER_WIDTH = 15.0f;

private bool m_hasGetGUIStyle = false;

private GUIStyle m_scrollerStyle = null;
private GUIStyle m_buttonElementStyle = null;

private UnityEngine.Object m_selectedUnityObject = null;
private System.Type m_uobjType = null;

public static void OpenWindow()
{
FindReferencesInProjectEditor window = GetWindow<FindReferencesInProjectEditor>();
Texture2D icon = EditorGUIUtility.Load("icons/UnityEditor.ConsoleWindow.png") as Texture2D;
window.titleContent = new GUIContent("reference find result", icon);
Vector2 minSize = new Vector2(500.0f, 300.0f);
window.minSize = minSize;
}

public static FindReferencesInProjectEditor GetWindow()
{
return GetWindow<FindReferencesInProjectEditor>();
}

public void SetPaths(in string[] paths)
{
m_targetPaths = paths;
}

public void SetTargetUobject(UnityEngine.Object uobject)
{
m_selectedUnityObject = uobject;
}

private void DrawContent()
{
// only use half space of this shit window
m_viewportRect = new Rect(0.0f, 0.0f, this.position.width - SCROLLER_WIDTH, this.position.height * 0.8f);
m_listRect = new Rect(0.0f, 0.0f, m_viewportRect.width, m_viewportRect.height);
int elementCount = m_targetPaths.Length;

if (elementCount < 1)
return;

GUI.BeginClip(m_listRect); // to clip the overflow stuff
int indexOffset = Mathf.FloorToInt(m_scrollPosition / ELEMENT_HEIGHT);
int showCount = Mathf.CeilToInt(m_listRect.height / ELEMENT_HEIGHT);
showCount = showCount > elementCount ? elementCount : showCount;
float startPosY = (indexOffset * ELEMENT_HEIGHT) - m_scrollPosition;

for (int i = 0; i < showCount; i++)
{
Rect elementRect = new Rect(0, 0 + startPosY + i * ELEMENT_HEIGHT, m_listRect.width, ELEMENT_HEIGHT);
DrawElement(elementRect, indexOffset + i);
}
GUI.EndClip();
}

private void DrawElement(Rect elementRect, int dataIndex)
{
if (dataIndex < m_targetPaths.Length && GUI.Button(elementRect, $"path {m_targetPaths[dataIndex]} ;", m_buttonElementStyle))
{
Debug.Log($"{m_targetPaths[dataIndex]}");
UnityEngine.Object targetObj = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(m_targetPaths[dataIndex]);
EditorGUIUtility.PingObject(targetObj);
}
}

private void DrawScroller()
{
// draw scroller on the right correctly
int elementCount = m_targetPaths.Length;
float fullElementHeight = elementCount * ELEMENT_HEIGHT;

Rect scrollbarRect = new Rect(m_viewportRect.x + m_viewportRect.width, m_viewportRect.y, SCROLLER_WIDTH, m_viewportRect.height);
m_scrollPosition = Mathf.Max(0, GUI.VerticalScrollbar(scrollbarRect, m_scrollPosition, m_listRect.height, 0, Mathf.Max(fullElementHeight, m_listRect.height)));

int controlId = GUIUtility.GetControlID(FocusType.Passive);
float scrollSensitivity = ELEMENT_HEIGHT;
float maxScrollPos = (fullElementHeight > m_listRect.height) ? (fullElementHeight - m_listRect.height) : 0;

if (EventType.ScrollWheel == Event.current.GetTypeForControl(controlId))
{
m_scrollPosition = Mathf.Clamp(m_scrollPosition + Event.current.delta.y * scrollSensitivity, 0, maxScrollPos);
Event.current.Use();
}
}

private void GetGUIStyle()
{
// scroller
m_scrollerStyle = new GUIStyle(GUI.skin.verticalScrollbar);
m_scrollerStyle.stretchWidth = false;
m_scrollerStyle.fixedWidth = SCROLLER_WIDTH;

// button element
m_buttonElementStyle = new GUIStyle(GUI.skin.button);
m_buttonElementStyle.alignment = TextAnchor.MiddleLeft;

m_uobjType = typeof(UnityEngine.Object);
}

private void DrawBottom()
{
m_bottomRect = new Rect(0.0f, this.position.height * 0.8f, this.position.width, this.position.height * 0.2f);
Rect fieldRect = new Rect(10.0f, m_bottomRect.y + 10.0f, 100.0f, 20.0f);

if (null == m_uobjType)
m_uobjType = typeof(UnityEngine.Object);

m_selectedUnityObject = EditorGUI.ObjectField(fieldRect, m_selectedUnityObject, m_uobjType, true);

Rect buttonRect = new Rect(150.0f, m_bottomRect.y, 200.0f, 40.0f);
if (GUI.Button(buttonRect, "try find reference"))
{
FindObjectReference();
}

}

private void FindObjectReference()
{
if (null == m_selectedUnityObject)
{
return;
}

Clear();
FindReferencesInProject.TryFindReferenceOfUobject(m_selectedUnityObject);
}

private void Clear()
{
m_targetPaths = System.Array.Empty<string>();
}

private void OnGUI()
{
if (!m_hasGetGUIStyle)
{
GetGUIStyle();
m_hasGetGUIStyle = true;
}

DrawContent();
DrawScroller();
DrawBottom();
}

}

搜索结果我选择使用一个简单的 editor window 来显示。
同时还能填入新的目标物体再做一次搜索。

总结

最近都没有做编辑器这边的开发,感觉直接退回了起点。感觉Unity编辑器的文档没有想象中那么详细,很多时候反而没有问同事来得方便。可能该提高自己的英文水平了。