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按钮可以选中全部我们筛选出的符合条件的物体,以下是实现代码:
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(); } } }