Unity 编辑器扩展:一键查找场景中组件引用关系(含完整源码)(组件引用查找工具实现笔记)
引言
Unity 编辑器内置了“Find References In Scene”功能,用于查找场景中特定组件的引用关系:选中组件右键,选择该选项,即可高亮所有使用该组件的 GameObject。 然而,该功能有时返回不相关结果(如所有脚本实例),且不支持一键全局查找或批量处理。 本工具扩展了这一功能,通过自定义 EditorWindow 实现一键查找:用户输入组件类型,工具扫描当前场景并列出所有引用该组件的 GameObject,支持高亮选中、导出列表和过滤。
工具基于 UnityEditor API(如 FindObjectsOfType
和 Selection
),适用于 Unity 2021.3+(推荐 2022.3 LTS),无需额外插件。原理:运行时反射获取组件类型,遍历场景 Hierarchy,收集引用关系。时间复杂度 O(n),n 为场景对象数,适用于中小型场景(大场景可分批处理)。
实现原理
- 查找机制:使用
FindObjectsOfType<T>()
获取所有指定组件实例,然后遍历其gameObject
收集引用路径。 - 可视化:EditorWindow 提供输入框(组件类型名)和结果列表,支持点击高亮 GameObject。
- 扩展性:支持过滤(如仅激活对象)、导出 JSON/CSV 和集成 Hierarchy 右键菜单。
- 局限性:仅查找当前打开场景;对于预制体引用需额外处理(使用 PrefabUtility);不包括项目视图中的资产引用(需 AssetDatabase.FindAssets)。
- 优化:缓存结果避免重复计算;异步搜索大场景避免编辑器卡顿。
完整源码
放置在 /Assets/Editor/ReferenceFinder.cs
(创建 Editor 文件夹)。
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
using System;
using System.Linq;
using System.IO;
public class ReferenceFinder : EditorWindow
{
private string componentName = "MeshRenderer"; // 默认组件类型
private Vector2 scrollPosition;
private List<GameObject> results = new List<GameObject>();
private bool filterActiveOnly = true;
private bool includePrefabs = false;
private bool showResults = false;
[MenuItem("Tools/Reference Finder")]
public static void OpenWindow()
{
GetWindow<ReferenceFinder>("Component Reference Finder");
}
private void OnGUI()
{
GUILayout.Label("组件引用查找工具", EditorStyles.boldLabel);
// 输入组件类型名
componentName = EditorGUILayout.TextField("组件类型名", componentName);
// 过滤选项
filterActiveOnly = EditorGUILayout.Toggle("仅激活对象", filterActiveOnly);
includePrefabs = EditorGUILayout.Toggle("包括预制体实例", includePrefabs);
EditorGUILayout.Space();
if (GUILayout.Button("查找引用"))
{
FindReferences();
}
if (showResults)
{
EditorGUILayout.Space();
GUILayout.Label($"找到 {results.Count} 个引用", EditorStyles.miniBoldLabel);
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
foreach (var go in results)
{
EditorGUILayout.BeginHorizontal();
GUILayout.Label(go.name);
if (GUILayout.Button("选中", GUILayout.Width(60)))
{
Selection.activeGameObject = go;
EditorGUIUtility.PingObject(go); // 高亮在 Hierarchy
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndScrollView();
EditorGUILayout.Space();
if (GUILayout.Button("导出结果 (JSON)"))
{
ExportResults();
}
}
}
private void FindReferences()
{
results.Clear();
showResults = true;
// 获取组件类型
Type componentType = GetComponentType(componentName);
if (componentType == null)
{
EditorUtility.DisplayDialog("错误", "无效的组件类型名", "OK");
return;
}
// 查找所有实例
Component[] components = FindObjectsOfType(componentType);
foreach (var comp in components)
{
GameObject go = comp.gameObject;
// 过滤
if (filterActiveOnly && !go.activeInHierarchy) continue;
if (!includePrefabs && PrefabUtility.IsPartOfPrefabInstance(go)) continue;
results.Add(go);
}
// 排序结果(按路径)
results = results.OrderBy(go => GetGameObjectPath(go)).ToList();
Repaint(); // 刷新窗口
}
private Type GetComponentType(string typeName)
{
// 支持内置组件和自定义脚本
Type type = Type.GetType(typeName + ", UnityEngine");
if (type == null)
{
type = Type.GetType(typeName + ", Assembly-CSharp"); // 项目脚本
}
return type;
}
private string GetGameObjectPath(GameObject go)
{
string path = go.name;
Transform parent = go.transform.parent;
while (parent != null)
{
path = parent.name + "/" + path;
parent = parent.parent;
}
return path;
}
private void ExportResults()
{
string jsonPath = EditorUtility.SaveFilePanel("导出结果", "", "references.json", "json");
if (string.IsNullOrEmpty(jsonPath)) return;
List<string> paths = results.Select(go => GetGameObjectPath(go)).ToList();
string json = JsonUtility.ToJson(new { references = paths });
File.WriteAllText(jsonPath, json);
EditorUtility.DisplayDialog("导出完成", "结果已导出到 " + jsonPath, "OK");
}
// 扩展:Hierarchy 右键菜单集成
[MenuItem("GameObject/Find Component References", true)]
private static bool ValidateFindReferences()
{
return Selection.activeGameObject != null;
}
[MenuItem("GameObject/Find Component References")]
private static void FindReferencesFromSelection()
{
Component[] components = Selection.activeGameObject.GetComponents<Component>();
if (components.Length == 0) return;
// 示例:查找第一个组件的引用
Type type = components[0].GetType();
FindObjectsOfType(type);
// 打开窗口显示结果
OpenWindow();
}
}
使用说明
- 安装:将源码保存为
ReferenceFinder.cs
在 Editor 文件夹。 - 打开工具:Tools > Reference Finder。
- 输入类型:如 “MeshRenderer”(内置)或 “MyCustomScript”(自定义)。
- 查找:点击“查找引用”,结果列表显示 GameObject 名,支持点击选中和高亮。
- 导出:结果可导出为 JSON 文件,便于分析。
- 右键集成:在 Hierarchy 选中 GameObject,右键 > Find Component References(查找其第一个组件的引用)。
扩展功能笔记
- 异步查找:大场景添加
EditorCoroutine
避免卡顿:
private IEnumerator AsyncFind(Type type)
{
Component[] components = FindObjectsOfType(type);
yield return null; // 分帧处理
// 继续结果收集
}
- 预制体支持:使用
PrefabUtility.GetPrefabInstanceHandle
检查预制体引用。 - 自定义过滤:添加搜索框过滤结果列表。
- 集成开源工具:如 Reference Finder(),可扩展为项目引用查找。
- 性能提示:大项目分场景查找;使用
Resources.FindObjectsOfTypeAll
包括非激活对象。
此工具简化了组件引用管理,适用于调试和优化。如果需要查找项目中资产引用,可扩展使用 AssetDatabase.FindAssets
。
“`