Unity 编辑器开发实战【Editor Window】- Filter 物体筛选工具

简介: Unity 编辑器开发实战【Editor Window】- Filter 物体筛选工具

Unity开发工作中,在Hierarchy窗口搜索栏可以通过物体名称或组件名称对场景中的物体进行搜索,但是并不能满足我们一些其它的搜索要求,例如搜索指定Tag标签的物体,或者指定Layer层级的物体,或者指定Active状态的物体,或者更为复杂的一些搜索,比如我们想找到场景中所有隐藏的、且挂有Camera组件的、且标签为MainCamera的物体,这些都无法实现。

今天分享一个作者为了解决上述搜索需求而开发的Filter物体筛选器:

image.gif

其中Target是指需要进行筛选的所有物体,All是指对场景中的所有物体进行筛选,也可以指定一个根级,对这个根物体的所有子物体进行筛选:

image.gif

确定好要进行筛选的物体后,下面来创建筛选条件:

image.gif

1.Name 通过物体名称的关键字进行筛选

image.gif

2.Component 通过组件进行筛选 -物体是否挂有指定组件

image.gif

3.Layer 通过物体的Layer层级进行筛选

image.gif

4.Tag 通过物体的Tag标签进行筛选

image.gif

5.Active 通过物体的活跃状态进行筛选

image.gif

以上是单个条件的筛选方式,我们也可以创建复合条件,即多个条件对物体进行筛选,比如文章开始提到的,我们要找到场景中所有隐藏的、且挂有Camera组件的、且标签为MainCamera的物体,需要创建3个条件:1.Active活跃状态为false条件、2.Component组件为Camera条件、3.Tag标签为MainCamera条件

image.gif

最终点击Select按钮可以选中全部我们筛选出的符合条件的物体,以下是实现代码:

usingUnityEngine;
usingUnityEditor;
usingUnityEngine.SceneManagement;
usingSystem;
usingSystem.Reflection;
usingSystem.Collections.Generic;
namespaceSK.Framework{
/// <summary>/// 过滤类型/// </summary>publicenumFilterMode    {
Name, //根据名字筛选Component, //根据组件筛选Layer, //根据层级筛选Tag, //根据标签筛选Active, //根据是否活跃筛选Missing, //丢失筛选    }
publicenumMissingMode    {
Material, //材质丢失Mesh, //网格丢失Script//脚本丢失    }
    [SerializeField]
publicclassFilterCondition    {
publicFilterModefilterMode;
publicMissingModemissingMode;
publicstringstringValue;
publicintintValue;
publicboolboolValue;
publicTypetypeValue;
publicFilterCondition(FilterModefilterMode, stringstringValue)
        {
this.filterMode=filterMode;
this.stringValue=stringValue;
        }
publicFilterCondition(FilterModefilterMode, intintValue)
        {
this.filterMode=filterMode;
this.intValue=intValue;
        }
publicFilterCondition(FilterModefilterMode, boolboolValue)
        {
this.filterMode=filterMode;
this.boolValue=boolValue;
        }
publicFilterCondition(FilterModefilterMode, TypetypeValue)
        {
this.filterMode=filterMode;
this.typeValue=typeValue;
        }
publicFilterCondition(FilterModefilterMode, MissingModemissingMode)
        {
this.filterMode=filterMode;
this.missingMode=missingMode;
        }
/// <summary>/// 判断物体是否符合条件/// </summary>/// <param name="target">物体</param>/// <returns>符合条件返回true,否则返回false</returns>publicboolIsMatch(GameObjecttarget)
        {
switch (filterMode)
            {
caseFilterMode.Name: returntarget.name.ToLower().Contains(stringValue.ToLower());
caseFilterMode.Component: returntarget.GetComponent(typeValue) !=null;
caseFilterMode.Layer: returntarget.layer==intValue;
caseFilterMode.Tag: returntarget.CompareTag(stringValue);
caseFilterMode.Active: returntarget.activeSelf==boolValue;
caseFilterMode.Missing:
switch (missingMode)
                    {
caseMissingMode.Material:
varmr=target.GetComponent<MeshRenderer>();
if (mr==null) returnfalse;
Material[] materials=mr.sharedMaterials;
boolflag=false;
for (inti=0; i<materials.Length; i++)
                            {
if(materials[i] ==null)
                                {
flag=true;
break;
                                }
                            }
returnflag;
caseMissingMode.Mesh:
varmf=target.GetComponent<MeshFilter>();
if (mf==null) returnfalse;
returnmf.sharedMesh==null;
caseMissingMode.Script:
Component[] components=target.GetComponents<Component>();
boolretV=false; 
for (inti=0; i<components.Length; i++)
                            {
if(components[i] ==null)
                                {
retV=true;
break;
                                }
                            }
returnretV;
default:
returnfalse;
                    }
default: returnfalse;
            }
        }
    }
publicsealedclassFilter : EditorWindow    {
        [MenuItem("SKFramework/Tools/Filter")]
privatestaticvoidOpen()
        {
varwindow=GetWindow<Filter>();
window.titleContent=newGUIContent("Filter");
window.Show();
        }
//筛选的目标privateenumFilterTarget        {
All, //在所有物体中筛选Specified, //在指定根级物体内筛选        }
privateFilterTargetfilterTarget=FilterTarget.All;
//存储所有筛选条件privatereadonlyList<FilterCondition>filterConditions=newList<FilterCondition>();
//指定的筛选根级privateTransformspecifiedTarget;
//存储所有组件类型privateList<Type>components;
//存储所有组件名称privateList<string>componentsNames;
privatereadonlyList<GameObject>selectedObjects=newList<GameObject>();
privateVector2scroll=Vector2.zero;
privatevoidOnEnable()
        {
components=newList<Type>();
componentsNames=newList<string>();
Assembly[] assemblies=AppDomain.CurrentDomain.GetAssemblies();
for (inti=0; i<assemblies.Length; i++)
            {
vartypes=assemblies[i].GetTypes();
for (intj=0; j<types.Length; j++)
                {
if (types[j].IsSubclassOf(typeof(Component)))
                    {
components.Add(types[j]);
componentsNames.Add(types[j].Name);
                    }
                }
            }
        }
privatevoidOnGUI()
        {
OnTargetGUI();
scroll=EditorGUILayout.BeginScrollView(scroll);
OnConditionGUI();
OnIsMatchedGameObjectsGUI();
EditorGUILayout.EndScrollView();
OnFilterGUI();
        }
privatevoidOnTargetGUI()
        {
filterTarget= (FilterTarget)EditorGUILayout.EnumPopup("Target", filterTarget);
switch (filterTarget)
            {
caseFilterTarget.Specified:
specifiedTarget=EditorGUILayout.ObjectField("Root", specifiedTarget, typeof(Transform), true) asTransform;
break;
            }
EditorGUILayout.Space();
        }
privatevoidOnConditionGUI()
        {
if (GUILayout.Button("Create New Condition", "DropDownButton"))
            {
GenericMenugm=newGenericMenu();
gm.AddItem(newGUIContent("Name"), false, () =>filterConditions.Add(newFilterCondition(FilterMode.Name, "GameObject")));
gm.AddItem(newGUIContent("Component"), false, () =>filterConditions.Add(newFilterCondition(FilterMode.Component, typeof(Transform))));
gm.AddItem(newGUIContent("Layer"), false, () =>filterConditions.Add(newFilterCondition(FilterMode.Layer, 0)));
gm.AddItem(newGUIContent("Tag"), false, () =>filterConditions.Add(newFilterCondition(FilterMode.Tag, "Untagged")));
gm.AddItem(newGUIContent("Active"), false, () =>filterConditions.Add(newFilterCondition(FilterMode.Active, true)));
gm.AddItem(newGUIContent("Missing / Material"), false, () =>filterConditions.Add(newFilterCondition(FilterMode.Missing, MissingMode.Material)));
gm.AddItem(newGUIContent("Missing / Mesh"), false, () =>filterConditions.Add(newFilterCondition(FilterMode.Missing, MissingMode.Mesh)));
gm.AddItem(newGUIContent("Missing / Script"), false, () =>filterConditions.Add(newFilterCondition(FilterMode.Missing, MissingMode.Script)));
gm.ShowAsContext();
            }
EditorGUILayout.Space();
if(filterConditions.Count>0)
            {
GUILayout.BeginVertical("Badge");
for (inti=0; i<filterConditions.Count; i++)
                {
varcondition=filterConditions[i];
GUILayout.BeginHorizontal();
if(filterConditions.Count>1)
GUILayout.Label($"{i + 1}.", GUILayout.Width(30f));
switch (condition.filterMode)
                    {
caseFilterMode.Name:
GUILayout.Label("Name", GUILayout.Width(80f));
condition.stringValue=EditorGUILayout.TextField(condition.stringValue);
break;
caseFilterMode.Component:
varindex=componentsNames.FindIndex(m=>m==condition.typeValue.Name);
GUILayout.Label("Component", GUILayout.Width(80f));
varnewIndex=EditorGUILayout.Popup(index, componentsNames.ToArray());
if (index!=newIndex) condition.typeValue=components[newIndex];
break;
caseFilterMode.Layer:
GUILayout.Label("Layer", GUILayout.Width(80f));
condition.intValue=EditorGUILayout.LayerField(condition.intValue);
break;
caseFilterMode.Tag:
GUILayout.Label("Tag", GUILayout.Width(80f));
condition.stringValue=EditorGUILayout.TagField(condition.stringValue);
break;
caseFilterMode.Active:
GUILayout.Label("Active", GUILayout.Width(80f));
condition.boolValue=EditorGUILayout.Toggle(condition.boolValue);
break;
caseFilterMode.Missing:
GUILayout.Label("Missing", GUILayout.Width(80f));
condition.missingMode= (MissingMode)EditorGUILayout.EnumPopup(condition.missingMode);
break;
default:
break;
                    }
if (GUILayout.Button("×", "MiniButton", GUILayout.Width(20f)))
                    {
filterConditions.RemoveAt(i);
return;
                    }
GUILayout.EndHorizontal();
                }
GUILayout.EndVertical();
            }
EditorGUILayout.Space();
        }
privatevoidOnIsMatchedGameObjectsGUI()
        {
for (inti=0; i<selectedObjects.Count; i++)
            {
GameObjectobj=selectedObjects[i];
if(obj==null)
                {
selectedObjects.RemoveAt(i);
i--;
continue;
                }
GUILayout.BeginHorizontal("IN Title");
GUILayout.Label(obj.name);
GUILayout.EndHorizontal();
if (Event.current.type==EventType.MouseDown&&GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition))
                {
EditorGUIUtility.PingObject(obj);
                }
            }
GUILayout.FlexibleSpace();
        }
privatevoidOnFilterGUI()
        {
GUILayout.BeginHorizontal();
if (GUILayout.Button("Filter", "ButtonLeft"))
            {
selectedObjects.Clear();
List<GameObject>targetGameObjects=newList<GameObject>();
switch (filterTarget)
                {
caseFilterTarget.All:
GameObject[] rootGameObjects=SceneManager.GetActiveScene().GetRootGameObjects();
for (inti=0; i<rootGameObjects.Length; i++)
                        {
varroot=rootGameObjects[i];
varallChildren=root.GetComponentsInChildren<Transform>(true);
for (intj=0; j<allChildren.Length; j++)
                            {
EditorUtility.DisplayProgressBar("Filter", allChildren[j].name, (float)i/rootGameObjects.Length);
targetGameObjects.Add(allChildren[j].gameObject);
                            }
                        }
EditorUtility.ClearProgressBar();
break;
caseFilterTarget.Specified:
Transform[] children=specifiedTarget.GetComponentsInChildren<Transform>(true);
for (inti=0; i<children.Length; i++)
                        {
EditorUtility.DisplayProgressBar("Filter", children[i].name, (float)i/children.Length);
targetGameObjects.Add(children[i].gameObject);
                        }
EditorUtility.ClearProgressBar();
break;
default:
break;
                }
for (inti=0; i<targetGameObjects.Count; i++)
                {
GameObjecttarget=targetGameObjects[i];
boolisMatch=true;
for (intj=0; j<filterConditions.Count; j++)
                    {
if (!filterConditions[j].IsMatch(target))
                        {
isMatch=false;
break;
                        }
                    }
EditorUtility.DisplayProgressBar("Filter", $"{target.name} -> Is Matched : {isMatch}", (float)i/targetGameObjects.Count);
if (isMatch)
                    {
selectedObjects.Add(target);
                    }
                }
EditorUtility.ClearProgressBar();
            }
if (GUILayout.Button("Select", "ButtonMid"))
            {
Selection.objects=selectedObjects.ToArray();
            }
if (GUILayout.Button("Clear", "ButtonRight"))
            {
selectedObjects.Clear();
            }
GUILayout.EndHorizontal();
        }
    }
}

image.gif

目录
相关文章
|
6天前
|
JavaScript 前端开发 API
vue3 v-md-editor markdown编辑器(VMdEditor)和预览组件(VMdPreview )的使用
本文介绍了如何在Vue 3项目中使用v-md-editor组件库来创建markdown编辑器和预览组件。文章提供了安装步骤、如何在main.js中进行全局配置、以及如何在页面中使用VMdEditor和VMdPreview组件的示例代码。此外,还提供了一个完整示例的链接,包括编辑器和预览组件的使用效果和代码。
vue3 v-md-editor markdown编辑器(VMdEditor)和预览组件(VMdPreview )的使用
|
2月前
|
图形学 开发者 存储
超越基础教程:深度拆解Unity地形编辑器的每一个隐藏角落,让你的游戏世界既浩瀚无垠又细节满满——从新手到高手的全面技巧升级秘籍
【8月更文挑战第31天】Unity地形编辑器是游戏开发中的重要工具,可快速创建复杂多变的游戏环境。本文通过比较不同地形编辑技术,详细介绍如何利用其功能构建广阔且精细的游戏世界,并提供具体示例代码,展示从基础地形绘制到植被与纹理添加的全过程。通过学习这些技巧,开发者能显著提升游戏画面质量和玩家体验。
67 3
|
2月前
|
开发者 图形学 开发工具
Unity编辑器神级扩展攻略:从批量操作到定制Inspector界面,手把手教你编写高效开发工具,解锁编辑器隐藏潜能
【8月更文挑战第31天】Unity是一款强大的游戏开发引擎,支持多平台发布与高度可定制的编辑器环境。通过自定义编辑器工具,开发者能显著提升工作效率。本文介绍如何使用C#脚本扩展Unity编辑器功能,包括批量调整游戏对象位置、创建自定义Inspector界面及项目统计窗口等实用工具,并提供具体示例代码。理解并应用这些技巧,可大幅优化开发流程,提高生产力。
126 1
|
2月前
|
开发者 图形学 Java
揭秘Unity物理引擎核心技术:从刚体动力学到关节连接,全方位教你如何在虚拟世界中重现真实物理现象——含实战代码示例与详细解析
【8月更文挑战第31天】Unity物理引擎对于游戏开发至关重要,它能够模拟真实的物理效果,如刚体运动、碰撞检测及关节连接等。通过Rigidbody和Collider组件,开发者可以轻松实现物体间的互动与碰撞。本文通过具体代码示例介绍了如何使用Unity物理引擎实现物体运动、施加力、使用关节连接以及模拟弹簧效果等功能,帮助开发者提升游戏的真实感与沉浸感。
39 1
|
1月前
一款非常棒的十六进制编辑器 —— 010 Editor
一款非常棒的十六进制编辑器 —— 010 Editor
|
1月前
|
图形学 C++ C#
Unity插件开发全攻略:从零起步教你用C++扩展游戏功能,解锁Unity新玩法的详细步骤与实战技巧大公开
【8月更文挑战第31天】Unity 是一款功能强大的游戏开发引擎,支持多平台发布并拥有丰富的插件生态系统。本文介绍 Unity 插件开发基础,帮助读者从零开始编写自定义插件以扩展其功能。插件通常用 C++ 编写,通过 Mono C# 运行时调用,需在不同平台上编译。文中详细讲解了开发环境搭建、简单插件编写及在 Unity 中调用的方法,包括创建 C# 封装脚本和处理跨平台问题,助力开发者提升游戏开发效率。
48 0
|
2月前
|
开发者 图形学 API
从零起步,深度揭秘:运用Unity引擎及网络编程技术,一步步搭建属于你的实时多人在线对战游戏平台——详尽指南与实战代码解析,带你轻松掌握网络化游戏开发的核心要领与最佳实践路径
【8月更文挑战第31天】构建实时多人对战平台是技术与创意的结合。本文使用成熟的Unity游戏开发引擎,从零开始指导读者搭建简单的实时对战平台。内容涵盖网络架构设计、Unity网络API应用及客户端与服务器通信。首先,创建新项目并选择适合多人游戏的模板,使用推荐的网络传输层。接着,定义基本玩法,如2D多人射击游戏,创建角色预制件并添加Rigidbody2D组件。然后,引入网络身份组件以同步对象状态。通过示例代码展示玩家控制逻辑,包括移动和发射子弹功能。最后,设置服务器端逻辑,处理客户端连接和断开。本文帮助读者掌握构建Unity多人对战平台的核心知识,为进一步开发打下基础。
66 0
|
2月前
|
开发者 图形学 C#
揭秘游戏沉浸感的秘密武器:深度解析Unity中的音频设计技巧,从背景音乐到动态音效,全面提升你的游戏氛围艺术——附实战代码示例与应用场景指导
【8月更文挑战第31天】音频设计在游戏开发中至关重要,不仅能增强沉浸感,还能传递信息,构建氛围。Unity作为跨平台游戏引擎,提供了丰富的音频处理功能,助力开发者轻松实现复杂音效。本文将探讨如何利用Unity的音频设计提升游戏氛围,并通过具体示例代码展示实现过程。例如,在恐怖游戏中,阴森的背景音乐和突然的脚步声能增加紧张感;在休闲游戏中,轻快的旋律则让玩家感到愉悦。
43 0
|
3月前
|
开发工具
vi编辑器,现在vi\vim是文本文件进行编辑的最佳选择,Vim是vi的加强的版本,兼容vi的所有指令,vim编辑器有三种工作模式,一开始进入的是命令模式,命令模式i是插入的意思,两下y+p复制内容
vi编辑器,现在vi\vim是文本文件进行编辑的最佳选择,Vim是vi的加强的版本,兼容vi的所有指令,vim编辑器有三种工作模式,一开始进入的是命令模式,命令模式i是插入的意思,两下y+p复制内容
|
4月前
|
开发工具
Vim 编辑器:高效文本编辑的瑞士军刀
**Vim 概览:** Vim 是一个功能丰富的文本编辑器,以其高度可定制性著称。文章介绍了 Vim 的高效使用技巧,包括快捷打开文件、命令行模式下的常用命令、查找与替换、删除和复制文本。还讨论了配置 `.vimrc` 文件以自定义设置,如改变 leader 键、设置缩进和高亮,并展示了安装插件如 vim-airline 和 vim-snazzy 的方法。通过这些技巧,用户能提升 Vim 使用效率。
47 5
下一篇
无影云桌面