Unity编辑器拓展_______查找一个场景中,游戏物体 “被引用的物体” 被其他物体引用的情况。

所用版本:Unity 2018.1.9

目的:在编辑器内非运行状态下,查找一个场景中,游戏物体 “被引用的物体” 被场景内其他物体持久化引用的情况。(持久化就是拖拽的那种,比如一个公开变量,你拖拽赋值),如图:

目前引用查询的本领大小:

可以查询自定义脚本组件的公开变量是否有引用游戏物体 “被引用的物体”,见上图:

可以查询自定义脚本组件的公开UnityEvent事件是否有引用游戏物体 “被引用的物体”:

可以查询Button组件的OnClick事件是否有引用游戏物体 “被引用的物体”:

提一嘴,场景中,就我展示的这三个游戏物体引用了游戏物体  "被引用的物体" 。像“公开变量——空”和“公开事件——空”,是没有引用游戏物体 "被引用的物体" 的。如图:

而其他的那些个游戏物体,就是来凑个数的,它们没有引用任何人。

操作方法:选中你的目标游戏物体,然后鼠标右键单击,调出菜单,选择 “查找场景内引用(只支持单个物体查询)” 按钮按下去就行了。

结果:会在控制台,把引用了游戏物体 “被引用的物体” 的 那些游戏物体名称一一打印出来。

如上所述,就是这么一个功能。不算难,但挺繁琐。我的三种引用查找方式,基本上能满足大部分需求,如果想要再拓展,在我的脚本基础上拓展就行了。不想拓展的话,创建个脚本,把我的代码复制下,然后把脚本放到Editor文件夹就行了。

下面就是我的代码:

using UnityEngine;
using UnityEditor;
using System.Reflection;
using UnityEngine.UI;
using UnityEngine.Events;
using System.Collections;
using System;
using System.Collections.Generic;
using System.Linq;


/// <summary>
/// 查找被选中的单个GameObject在场景中所有被其他脚本、事件引用的地方
/// </summary>
public class FindGameObjectInScene
{

    /// <summary>
    /// 获取当前被选中的游戏物体的InstanceID,这个值是系统自动赋予的,且是唯一的。
    /// </summary>
    static int SelectedObjID=0;

    /// <summary>
    /// 记录总共有多少个脚本、事件等引用了当前被“鼠标选中的游戏物体”
    /// </summary>
    static int referenceCount = 0;

    /// <summary>
    /// 假设你要搜查的是一个Button组件,Button组件引用其他物体的方式,就是给点击事件上引用游戏物体或游戏物体的组件。
    /// 这个变量是记录按钮组件的点击事件上绑定方法数量的。
    /// </summary>
    static int buttonEventMethodCount = 0;


    //假如被搜查的游戏物体身上有个自定义的组件,组件中有UnityEvent事件类型的公开变量,就需要去再搜查一遍
    //这个事件上绑定的方法。 思路和搜索按钮事件一样。
    /// <summary>
    /// 这个变量是记录自定义组件的UnityEvent事件上绑定方法数量的。
    /// </summary>
    static int unityEventMethodCount = 0;






    [MenuItem("GameObject/查找场景内引用(只支持单个物体查询)", false, priority = -1)]
    static void StartFindReference()
    {

        //开始标记,输出的文字颜色为绿色
        Debug.Log($"-><color=#006400>开始查找游戏物体</color> <color=#FF0000>“{Selection.transforms[0].gameObject.name}”</color> <color=#006400>在场景内的所有引用!!!</color>");

        //获取当前被选中的游戏物体的InstanceID,这个值是系统自动赋予的,且是唯一的。
        SelectedObjID = Selection.transforms[0].gameObject.GetInstanceID();

        //获取当前场景中所有的游戏物体
        List<GameObject>currentSceneAllGameObject = GetAllSceneObjectsWithInactive();

        //从列表中移除“鼠标选中的游戏物体”,没有自己搜查自己的必要
        currentSceneAllGameObject.Remove(Selection.transforms[0].gameObject);

        //输出场景中游戏物体总数
        Debug.Log($"-><color=#006400>场景中共有</color> <color=#FF0000> {currentSceneAllGameObject.Count} </color> <color=#006400>个游戏物体,不包括“鼠标选中物体”本身!!!</color>");

        //循环执行,对每一个场景中的游戏物体进行筛选查找,判断它是否有引用当前被鼠标指针选中的游戏物体
        for (int i = 0; i < currentSceneAllGameObject.Count; i++)
        {
            GameObject go = currentSceneAllGameObject[i];
            Find(go);
        }

        //输出场景中,有引用“鼠标选中的物体”的游戏物体数量
        Debug.Log($"-><color=#006400>“鼠标选中的物体”被</color> <color=#FF0000> {referenceCount} </color> <color=#006400>个游戏物体所引用</color>");

        //重置化全局变量
        SelectedObjID = 0;
        referenceCount = 0;
        buttonEventMethodCount = 0;
        unityEventMethodCount = 0;


        //结束标记,输出的文字颜色为绿色
        Debug.Log($"-><color=#006400>{"查找结束!!!"}</color>");

    }



    [MenuItem("GameObject/查找场景内引用(只支持单个物体查询)", true)]
    static bool StartFindReferenceValidate()
    {
        //假如有选中场景中的物体,且只选中了一个
        return Selection.transforms.Length == 1;
    }




    //用于获取所有Hierarchy中的物体,包括激活的和被禁用的物体
    private static List<GameObject> GetAllSceneObjectsWithInactive()
    {
        var allTransforms = Resources.FindObjectsOfTypeAll(typeof(Transform));
        var previousSelection = Selection.objects;
        Selection.objects = allTransforms.Cast<Transform>()
            //C#中Linq的一些操作
            .Where(x => x != null)
            .Select(x => x.gameObject)
            //如果你只想获取 所有 在Hierarchy中 被禁用 的物体,反注释下面一句代码
            //.Where(x => x != null && !x.activeInHierarchy)
            .Cast<UnityEngine.Object>().ToArray();

        var selectedTransforms = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);
        Selection.objects = previousSelection;
        return selectedTransforms.Select(tr => tr.gameObject).ToList();

    }


    /// <summary>
    /// 获得GameObject在Hierarchy中的完整路径
    /// </summary>
     static string GetTransPath(Transform trans)
    {
        if (!trans.parent)
        {
            return trans.name;

        }
        return GetTransPath(trans.parent) + "/" + trans.name;
    }




    /// <summary>
    /// 查找当前循环到的游戏物体,是否有引用被鼠标选中的那个游戏物体
    /// </summary>
    /// <param name="go"></param>
    static void Find(GameObject go)
    { 

        //获取当前循环到的游戏物体身上的所有组件
        Component[] components = go.GetComponents<Component>();

        //循环遍历每一个组件,看他们之中的某个变量是否有引用“鼠标选中的游戏物体”
        for (int i = 0; i<components.Length; i++)
        {

            //假设当前组件为UGUI按钮类型
            if(components[i].GetType()== typeof(Button))
            {

                //拿到当前Button组件的引用
                Button currentButton = components[i] as Button;

                //设置当前按钮组件点击事件上绑定方法的数量  GetPersistentEventCount为获得持久化方法数量函数
                buttonEventMethodCount = currentButton.onClick.GetPersistentEventCount();

                //循环遍历按钮点击事件列表,从事件列表中找一下,
                //看列表中是否包含我们“鼠标选中的游戏物体”所拥有的方法,如果包含的话,
                //就代表这个按钮组件,或者说拥有这个按钮组件的游戏物体,有引用“鼠标选中的游戏物体”
                for (int m = 0; m < buttonEventMethodCount; m++)
                {
                    //判断被按钮事件引用的究竟是游戏物体 ,还是游戏物体上挂载的组件
                    UnityEngine.Object PersistentTarget = currentButton.onClick.GetPersistentTarget(m);

                    if(PersistentTarget==null)
                    {
                        continue;
                    }
                    //被引用的是游戏物体(GameObject)类型 
                    if (PersistentTarget is GameObject)
                    {
                        //直接将游戏物体的ID和“鼠标选中的游戏物体”的ID进行比对,判断是否一致
                        if (PersistentTarget.GetInstanceID() == SelectedObjID)
                        {
                            referenceCount += 1;
                            Debug.Log($"-><color=#006400>游戏物体</color> <color=#FF0000> {GetTransPath(go.transform)} </color> <color=#006400>引用了被查找的物体</color>");
                            return; 
                        }
                    }
                    //被引用的是组件(Component)类型
                    else if (PersistentTarget is Component)
                    {
                        //将被引用的Object转化为组件
                        Component PersistentTargetComponent = PersistentTarget as Component;

                        //首先获取组件对应的游戏物体,再将游戏物体ID和“鼠标选中的游戏物体”的ID进行比对,判断是否一致
                        if (PersistentTargetComponent.gameObject.GetInstanceID() == SelectedObjID)
                        {
                            referenceCount += 1;
                            Debug.Log($"-><color=#006400>游戏物体</color> <color=#FF0000> {GetTransPath(go.transform)} </color> <color=#006400>引用了被查找的物体</color>");
                            return;
                        }
                    }

                }
            }

            //假设当前被搜查的游戏物体的当前组件为未知类型,或者说,是你无法指定的类型
            else
            {

                //无法预测被搜查的组件是什么类型,就只能用基类Component来存储引用了
                Component component = components[i];

                //BindingFlags   修饰符标志,搜索组件内对应的条件的变量  可自行百度搜索看其含义
                //获取当前组件中,所有公开的成员变量
                FieldInfo[] fields = component.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);

                //循环遍历当前组件中的所有公开的成员变量
                for (int j = 0; j < fields.Length; j++)
                {
                    //尝试获取当前的公开成员变量的值
                    var value = fields[j].GetValue(component);

                    //进行安全性校验,判断是否为空
                    if (value ==null|| value.Equals(null))
                    {
                        continue;
                    }

                    //游戏物体(GameObject)类型
                    if (value is GameObject)
                    {
                        //直接将游戏物体的ID和“鼠标选中的游戏物体”的ID进行比对,判断是否一致
                        if ((value as GameObject).GetInstanceID() == SelectedObjID)
                        {
                            referenceCount += 1;
                            Debug.Log($"-><color=#006400>游戏物体</color> <color=#FF0000> {GetTransPath(go.transform)} </color> <color=#006400>引用了被查找的物体</color>");
                            return;
                        }
                    }

                    //组件(Component)类型
                    else if (value is Component)
                    {
                        //首先获取组件对应的游戏物体,再将游戏物体ID和“鼠标选中的游戏物体”的ID进行比对,判断是否一致
                        if ((value as Component).gameObject.GetInstanceID() == SelectedObjID)
                        {
                            referenceCount += 1;
                            Debug.Log($"-><color=#006400>游戏物体</color> <color=#FF0000> {GetTransPath(go.transform)} </color> <color=#006400>引用了被查找的物体</color>");
                            return;
                        }
                    }

                    //事件(UnityEvent)类型
                    else if (value is UnityEvent)
                    {
                        //将成员变量转化为UnityEvent类型
                        UnityEvent currentEvent = value as UnityEvent;
                       // 设置当前组件UnityEvent事件上绑定方法的数量   GetPersistentEventCount为获得持久化方法数量函数
                        unityEventMethodCount = currentEvent.GetPersistentEventCount();
                        for (int m = 0; m < unityEventMethodCount; m++)
                        {

                            //判断被UnityEvent事件引用的究竟是游戏物体 ,还是游戏物体上挂载的组件
                            UnityEngine.Object PersistentTarget = currentEvent.GetPersistentTarget(m);

                            if (PersistentTarget == null)
                            {
                                continue;
                            }

                            //被引用的是游戏物体(GameObject)类型
                            if (PersistentTarget is GameObject)
                            {
                                //直接将游戏物体的ID和“鼠标选中的游戏物体”的ID进行比对,判断是否一致
                                if (PersistentTarget.GetInstanceID() == SelectedObjID)
                                {
                                    referenceCount += 1;
                                    Debug.Log($"-><color=#006400>游戏物体</color> <color=#FF0000> {GetTransPath(go.transform)} </color> <color=#006400>引用了被查找的物体</color>");
                                    return;
                                }
                            }
                            //被引用的是组件(Component)类型
                            else if (PersistentTarget is Component)
                            {
                                //将被引用的Object转化为组件
                                Component PersistentTargetComponent = PersistentTarget as Component;

                                //首先获取组件对应的游戏物体,再将游戏物体ID和“鼠标选中的游戏物体”的ID进行比对,判断是否一致
                                if (PersistentTargetComponent.gameObject.GetInstanceID() == SelectedObjID)
                                {
                                    referenceCount += 1;
                                    Debug.Log($"-><color=#006400>游戏物体</color> <color=#FF0000> {GetTransPath(go.transform)} </color> <color=#006400>引用了被查找的物体</color>");
                                    return;
                                }
                            }
                        }
                    }

                }


            }

        }



    }

}

最后,补充一点,如果脚本哪里显示.Net版本不对的话,在这里设置成和我一致就行:

如果你觉得不错,麻烦点个赞呗!当然,有认为不对的地方,也可以指出来。

猜你喜欢

转载自blog.csdn.net/qq_37760273/article/details/109243395