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

举报
CoderZ1010 发表于 2022/09/25 06:50:35 2022/09/25
【摘要】     Unity开发工作中,在Hierarchy窗口搜索栏可以通过物体名称或组件名称对场景中的物体进行搜索,但是并不能满足我们一些其它的搜索要求,例如搜索指定Tag标签的物体,或者指定Layer层级的物体,或者指定Active状态的物体,或者更为复杂的一些搜索,比如我们想找到场景中所有隐藏的、且挂有Cam...

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

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

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

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

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

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

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

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

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

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

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


      using UnityEngine;
      using UnityEditor;
      using UnityEngine.SceneManagement;
      using System;
      using System.Reflection;
      using System.Collections.Generic;
      namespace SK.Framework
      {
         /// <summary>
         /// 过滤类型
         /// </summary>
         public enum FilterMode
          {
              Name, //根据名字筛选
              Component, //根据组件筛选
              Layer, //根据层级筛选
              Tag, //根据标签筛选
              Active, //根据是否活跃筛选
              Missing, //丢失筛选
          }
         public enum MissingMode
          {
              Material, //材质丢失
              Mesh, //网格丢失
              Script //脚本丢失
          }
          [SerializeField]
         public class FilterCondition
          {
             public FilterMode filterMode;
             public MissingMode missingMode;
             public string stringValue;
             public int intValue;
             public bool boolValue;
             public Type typeValue;
             public FilterCondition(FilterMode filterMode, string stringValue)
              {
                 this.filterMode = filterMode;
                 this.stringValue = stringValue;
              }
             public FilterCondition(FilterMode filterMode, int intValue)
              {
                 this.filterMode = filterMode;
                 this.intValue = intValue;
              }
             public FilterCondition(FilterMode filterMode, bool boolValue)
              {
                 this.filterMode = filterMode;
                 this.boolValue = boolValue;
              }
             public FilterCondition(FilterMode filterMode, Type typeValue)
              {
                 this.filterMode = filterMode;
                 this.typeValue = typeValue;
              }
             public FilterCondition(FilterMode filterMode, MissingMode missingMode)
              {
                 this.filterMode = filterMode;
                 this.missingMode = missingMode;
              }
             /// <summary>
             /// 判断物体是否符合条件
             /// </summary>
             /// <param name="target">物体</param>
             /// <returns>符合条件返回true,否则返回false</returns>
             public bool IsMatch(GameObject target)
              {
                 switch (filterMode)
                  {
                     case FilterMode.Name: return target.name.ToLower().Contains(stringValue.ToLower());
                     case FilterMode.Component: return target.GetComponent(typeValue) != null;
                     case FilterMode.Layer: return target.layer == intValue;
                     case FilterMode.Tag: return target.CompareTag(stringValue);
                     case FilterMode.Active: return target.activeSelf == boolValue;
                     case FilterMode.Missing:
                         switch (missingMode)
                          {
                             case MissingMode.Material:
                                 var mr = target.GetComponent<MeshRenderer>();
                                 if (mr == null) return false;
                                  Material[] materials = mr.sharedMaterials;
                                 bool flag = false;
                                 for (int i = 0; i < materials.Length; i++)
                                  {
                                     if(materials[i] == null)
                                      {
                                          flag = true;
                                         break;
                                      }
                                  }
                                 return flag;
                             case MissingMode.Mesh:
                                 var mf = target.GetComponent<MeshFilter>();
                                 if (mf == null) return false;
                                 return  mf.sharedMesh == null;
                             case MissingMode.Script:
                                  Component[] components = target.GetComponents<Component>();
                                 bool retV = false;
                                 for (int i = 0; i < components.Length; i++)
                                  {
                                     if(components[i] == null)
                                      {
                                          retV = true;
                                         break;
                                      }
                                  }
                                 return retV;
                             default:
                                 return false;
                          }
                     default: return false;
                  }
              }
          }
         public sealed class Filter : EditorWindow
          {
              [MenuItem("SKFramework/Tools/Filter")]
             private static void Open()
              {
                 var window = GetWindow<Filter>();
                  window.titleContent = new GUIContent("Filter");
                  window.Show();
              }
             //筛选的目标
             private enum FilterTarget
              {
                  All, //在所有物体中筛选
                  Specified, //在指定根级物体内筛选
              }
             private FilterTarget filterTarget = FilterTarget.All;
             //存储所有筛选条件
             private readonly List<FilterCondition> filterConditions = new List<FilterCondition>();
             //指定的筛选根级
             private Transform specifiedTarget;
             //存储所有组件类型
             private List<Type> components;
             //存储所有组件名称
             private List<string> componentsNames;
             private readonly List<GameObject> selectedObjects = new List<GameObject>();
             private Vector2 scroll = Vector2.zero;
             private void OnEnable()
              {
                  components = new List<Type>();
                  componentsNames = new List<string>();
                  Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
                 for (int i = 0; i < assemblies.Length; i++)
                  {
                     var types = assemblies[i].GetTypes();
                     for (int j = 0; j < types.Length; j++)
                      {
                         if (types[j].IsSubclassOf(typeof(Component)))
                          {
                              components.Add(types[j]);
                              componentsNames.Add(types[j].Name);
                          }
                      }
                  }
              }
             private void OnGUI()
              {
                  OnTargetGUI();
                  scroll = EditorGUILayout.BeginScrollView(scroll);
                  OnConditionGUI();
                  OnIsMatchedGameObjectsGUI();
                  EditorGUILayout.EndScrollView();
                  OnFilterGUI();
              }
             private void OnTargetGUI()
              {
                  filterTarget = (FilterTarget)EditorGUILayout.EnumPopup("Target", filterTarget);
                 switch (filterTarget)
                  {
                     case FilterTarget.Specified:
                          specifiedTarget = EditorGUILayout.ObjectField("Root", specifiedTarget, typeof(Transform), true) as Transform;
                         break;
                  }
                  EditorGUILayout.Space();
              }
             private void OnConditionGUI()
              {
                 if (GUILayout.Button("Create New Condition", "DropDownButton"))
                  {
                      GenericMenu gm = new GenericMenu();
                      gm.AddItem(new GUIContent("Name"), false, () => filterConditions.Add(new FilterCondition(FilterMode.Name, "GameObject")));
                      gm.AddItem(new GUIContent("Component"), false, () => filterConditions.Add(new FilterCondition(FilterMode.Component, typeof(Transform))));
                      gm.AddItem(new GUIContent("Layer"), false, () => filterConditions.Add(new FilterCondition(FilterMode.Layer, 0)));
                      gm.AddItem(new GUIContent("Tag"), false, () => filterConditions.Add(new FilterCondition(FilterMode.Tag, "Untagged")));
                      gm.AddItem(new GUIContent("Active"), false, () => filterConditions.Add(new FilterCondition(FilterMode.Active, true)));
                      gm.AddItem(new GUIContent("Missing / Material"), false, () => filterConditions.Add(new FilterCondition(FilterMode.Missing, MissingMode.Material)));
                      gm.AddItem(new GUIContent("Missing / Mesh"), false, () => filterConditions.Add(new FilterCondition(FilterMode.Missing, MissingMode.Mesh)));
                      gm.AddItem(new GUIContent("Missing / Script"), false, () => filterConditions.Add(new FilterCondition(FilterMode.Missing, MissingMode.Script)));
                      gm.ShowAsContext();
                  }
                  EditorGUILayout.Space();
                 if(filterConditions.Count > 0)
                  {
                      GUILayout.BeginVertical("Badge");
                     for (int i = 0; i < filterConditions.Count; i++)
                      {
                         var condition = filterConditions[i];
                          GUILayout.BeginHorizontal();
                         if(filterConditions.Count > 1)
                              GUILayout.Label($"{i + 1}.", GUILayout.Width(30f));
                         switch (condition.filterMode)
                          {
                             case FilterMode.Name:
                                  GUILayout.Label("Name", GUILayout.Width(80f));
                                  condition.stringValue = EditorGUILayout.TextField(condition.stringValue);
                                 break;
                             case FilterMode.Component:
                                 var index = componentsNames.FindIndex(m => m == condition.typeValue.Name);
                                  GUILayout.Label("Component", GUILayout.Width(80f));
                                 var newIndex = EditorGUILayout.Popup(index, componentsNames.ToArray());
                                 if (index != newIndex) condition.typeValue = components[newIndex];
                                 break;
                             case FilterMode.Layer:
                                  GUILayout.Label("Layer", GUILayout.Width(80f));
                                  condition.intValue = EditorGUILayout.LayerField(condition.intValue);
                                 break;
                             case FilterMode.Tag:
                                  GUILayout.Label("Tag", GUILayout.Width(80f));
                                  condition.stringValue = EditorGUILayout.TagField(condition.stringValue);
                                 break;
                             case FilterMode.Active:
                                  GUILayout.Label("Active", GUILayout.Width(80f));
                                  condition.boolValue = EditorGUILayout.Toggle(condition.boolValue);
                                 break;
                             case FilterMode.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();
              }
             private void OnIsMatchedGameObjectsGUI()
              {
                 for (int i = 0; i < selectedObjects.Count; i++)
                  {
                      GameObject obj = 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();
              }
             private void OnFilterGUI()
              {
                  GUILayout.BeginHorizontal();
                 if (GUILayout.Button("Filter", "ButtonLeft"))
                  {
                      selectedObjects.Clear();
                      List<GameObject> targetGameObjects = new List<GameObject>();
                     switch (filterTarget)
                      {
                         case FilterTarget.All:
                              GameObject[] rootGameObjects = SceneManager.GetActiveScene().GetRootGameObjects();
                             for (int i = 0; i < rootGameObjects.Length; i++)
                              {
                                 var root = rootGameObjects[i];
                                 var allChildren = root.GetComponentsInChildren<Transform>(true);
                                 for (int j = 0; j < allChildren.Length; j++)
                                  {
                                      EditorUtility.DisplayProgressBar("Filter", allChildren[j].name, (float)i / rootGameObjects.Length);
                                      targetGameObjects.Add(allChildren[j].gameObject);
                                  }
                              }
                              EditorUtility.ClearProgressBar();
                             break;
                         case FilterTarget.Specified:
                              Transform[] children = specifiedTarget.GetComponentsInChildren<Transform>(true);
                             for (int i = 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 (int i = 0; i < targetGameObjects.Count; i++)
                      {
                          GameObject target = targetGameObjects[i];
                         bool isMatch = true;
                         for (int j = 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();
              }
          }
      }
  
 

文章来源: coderz.blog.csdn.net,作者:CoderZ1010,版权归原作者所有,如需转载,请联系作者。

原文链接:coderz.blog.csdn.net/article/details/119643874

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。