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版本不对的话,在这里设置成和我一致就行:
如果你觉得不错,麻烦点个赞呗!当然,有认为不对的地方,也可以指出来。
本文地址:https://blog.csdn.net/qq_37760273/article/details/109243395
上一篇: 在Button类的派生类中增加属性以及用事件绑定类
下一篇: 292. Nim 游戏