Unity editor extension - find out which components the GameObject is referenced by


foreword

Sometimes it is necessary to find out whether a GameObject is referenced by which components in the scene. It is relatively inefficient to check directly, and it may be missed. Here is a small tool to find it.


1. Rendering

insert image description here

Two, ideas

If you want to know whether a component refers to our GameObject, you can get all the fields of this component, namely FieldInfo. Then traverse all the FiledInfo, if the FiledInfo is GameObject or Component type, then we can get the GameObject corresponding to this FieldInfo, and then judge whether this GameObject is the same as the GameObject we want to query. If yes, it means that this component refers to the GameObject we queried.

Three, realize

ok, now that we have an idea, let's implement this function.

1. Get all objects

var objs = Resources.FindObjectsOfTypeAll(typeof(Transform));

The objects found by the Resources.FindObjectsOfTypeAll() method include not only the Hierarchy window, but also the prefabs in the Project window. In general, we only want to find references in the scene, not prefabs. Otherwise, two identical copies will usually be found. Next we filter

2. Filter out some unwanted objects

2.1 Get only the Transform that is the root node

Why only get the Transform that is the root node. Because we can get the components of the root node and all its child nodes through rootTran.GetComponentsInChildren<Component>(). So getting all the root nodes is equivalent to getting all the components.

    private static List<Transform> GetRootTranList(Object[] objs)
    {
    
    
        List<Transform> parentTranList = new List<Transform>();

        foreach (var o in objs)
        {
    
    
            var tran = o as Transform;
            if (tran.parent == null || tran.parent.name.Equals(CANVAS_ENVIROMENT)) 
            {
    
    
                //过滤掉预制体
                if (IsInHierarchy(tran.gameObject))
                {
    
    
                    parentTranList.Add(tran);
                }
            }
        }

        return parentTranList;
    }

Look at the code above:

  if (tran.parent == null || tran.parent.name.Equals("Canvas (Environment)")) 

The root node can be judged by tran.parent == null, which is easy to understand.

Then why the name of the parent node is "Canvas (Environment)"? When we double-click a UGUI prefab, we can see its level in the Hierarchy window, but Unity will automatically add a "Canvas (Environment)" The root node, so its root node becomes this. In this case, we also want to query, so add it.

2.2 Filter preform

How to filter prefabs can be judged by gameObject.scene.path . The path of GameObject in the scene is not empty, but the prefab is empty.

So we can judge by whether it is empty or not .

It should be noted that the double-clicked UGUI prefab, that is, the root node mentioned above is "Canvas (Environment)", and this path is also empty, but we want to add it, so when the root node name is this name, We also return true. Such as the following code

    private static bool IsInHierarchy(GameObject go)
    {
    
    
        if (!string.IsNullOrEmpty(go.scene.path))
        {
    
    
            return true;
        }else if (go.name.Equals(CANVAS_ENVIROMENT))
        {
    
    
            return true;
        }

        return false;
    }

3. Determine whether all components in a root node reference the query object

Then comes the critical time, as in the previous idea, we get all the components of the root node. For any component, we get all its fields FiledInfo, and then judge the field FiledInfo (GameObject == the object we queried), if equal, that is, the component references the object we queried. code show as below

    private static bool FindOneReferenced(Transform rootTran, GameObject targetGo)
    {
    
    
        //获取这个节点和其子节点的所有组件
        Component[] coms = rootTran.GetComponentsInChildren<Component>();
        
        bool isFind = false;
        for (int i = 0; i < coms.Length; i++)
        {
    
                
            if (coms[i] == null)
            {
    
    
                continue;
            }
            //遍历一个组件的所有字段
            var fileList = coms[i].GetType().GetFields().ToList<FieldInfo>();
            for (int j = 0; j < fileList.Count; j++)
            {
    
    
                var fileInfo = fileList[j];
                var fileValue = fileInfo.GetValue(coms[i]); //关键代码,获得一个字段的引用对象
                GameObject fileGo = null;

                if (fileValue == null)
                {
    
    
                    continue;
                }
				
				//如果是GameObject
                if (typeof(GameObject) == fileValue.GetType())
                {
    
    
                    fileGo = (fileValue as GameObject)?.gameObject;
                }
                //如果继承了Component组件
                else if (typeof(Component).IsAssignableFrom(fileValue.GetType()))
                {
    
    
                    var tempComp = (fileValue as Component);
                    if (tempComp != null)
                        fileGo = tempComp.gameObject;
                    // fileGo = (fileValue as Component)?.gameObject; 如果改用此简写,可能会报 UnassignedReferenceException 异常...
                }

                if (fileGo != null && fileGo == curSelectedGo)
                {
    
    
                    isFind = true;
                    //在Hierarchy窗口显示查到的物体
                    EditorGUIUtility.PingObject(coms[i]);
                    Debug.Log($"找到引用,引用物体名:{coms[i]} 字段名:{fileInfo.Name}");
                }
            }
        }

        return isFind;
    }

4. Complete code

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using NUnit.Framework;
using UnityEditor;
using UnityEditor.Build.Content;
using UnityEngine;

public class FindTools
{
    
    
    private static GameObject curSelectedGo = null;
    private const string CANVAS_ENVIROMENT = "Canvas (Environment)"; //双击UGUI的预制体,在Hierarchy窗口,此预制体的根节点会变为这个名字

    [MenuItem("GameObject/FindReference", false, 30)]
    public static void FindReferenced()
    {
    
       
        //Resources.FindObjectsOfTypeAll()方法找到的物体不仅包括 Hierarchy窗口,也包括Project窗口里的预制体。接下来会过滤掉预制体
        var objs = Resources.FindObjectsOfTypeAll(typeof(Transform));

        GameObject selectedGo = Selection.activeGameObject;
        if (selectedGo == null)
        {
    
    
            Debug.Log("请先选中要查找的物体");
            return;
        }

        curSelectedGo = selectedGo;

        var parentTranList = GetRootTranList(objs);
        bool isFind = false;
        for (int i = 0; i < parentTranList.Count; i++)
        {
    
    
            isFind = FindOneReferenced(parentTranList[i], curSelectedGo) || isFind;
        }

        if (!isFind)
        {
    
    
            Debug.LogWarning("没能找到引用");
        }
    }

    private static List<Transform> GetRootTranList(Object[] objs)
    {
    
    
        List<Transform> parentTranList = new List<Transform>();

        foreach (var o in objs)
        {
    
    
            var tran = o as Transform;
            if (tran.parent == null || tran.parent.name.Equals(CANVAS_ENVIROMENT)) 
            {
    
    
                //过滤掉预制体
                if (IsInHierarchy(tran.gameObject))
                {
    
    
                    parentTranList.Add(tran);
                }
            }
        }

        return parentTranList;
    }

    /// <summary>
    /// 判断一个GameObject是否在场景中。 用于过滤掉预制体
    /// </summary>
    /// <param name="go"></param>
    /// <returns></returns>
    private static bool IsInHierarchy(GameObject go)
    {
    
    
        if (!string.IsNullOrEmpty(go.scene.path))
        {
    
    
            return true;
        }else if (go.name.Equals(CANVAS_ENVIROMENT))
        {
    
    
            return true;
        }

        return false;
    }

    private static bool FindOneReferenced(Transform rootTran, GameObject targetGo)
    {
    
    
        //获取这个节点和其子节点的所有组件
        Component[] coms = rootTran.GetComponentsInChildren<Component>();
        
        bool isFind = false;
        for (int i = 0; i < coms.Length; i++)
        {
    
                
            if (coms[i] == null)
            {
    
    
                continue;
            }
            
            //遍历一个组件的所有字段
            var fileList = coms[i].GetType().GetFields().ToList<FieldInfo>();
            for (int j = 0; j < fileList.Count; j++)
            {
    
    
                var fileInfo = fileList[j];
                var fileValue = fileInfo.GetValue(coms[i]); //关键代码,获得一个字段的引用对象
                GameObject fileGo = null;

                if (fileValue == null)
                {
    
    
                    continue;
                }

				//是否是GameObject
                if (typeof(GameObject) == fileValue.GetType())
                {
    
    
                    fileGo = (fileValue as GameObject)?.gameObject;
                }
                //或者是否继承Component组件
                else if (typeof(Component).IsAssignableFrom(fileValue.GetType()))
                {
    
    
                    var tempComp = (fileValue as Component);
                    if (tempComp != null)
                        fileGo = tempComp.gameObject;
                    // fileGo = (fileValue as Component)?.gameObject; 如果改用此简写,可能会报 UnassignedReferenceException 异常...
                }

                if (fileGo != null && fileGo == curSelectedGo)
                {
    
    
                    isFind = true;
                    //在Hierarchy窗口显示查到的物体
                    EditorGUIUtility.PingObject(coms[i]);
                    Debug.Log($"找到引用,引用物体名:{coms[i]} 字段名:{fileInfo.Name}");
                }
            }
        }

        return isFind;
    }
}

Summarize

The above is the content of this sharing, the main idea is to get all the fields FieldInfo, and then make a judgment. Everything else is based on this.

Guess you like

Origin blog.csdn.net/aaa27987/article/details/118032288