您当前的位置: 首页 >  unity

CoderZ1010

暂无认证

  • 4浏览

    0关注

    168博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

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

CoderZ1010 发布时间:2021-08-12 14:37:23 ,浏览量:4

    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
{
    /// 
    /// 过滤类型
    /// 
    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;
        }
        /// 
        /// 判断物体是否符合条件
        /// 
        /// 物体
        /// 符合条件返回true,否则返回false
        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();
                            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();
                            if (mf == null) return false;
                            return  mf.sharedMesh == null;
                        case MissingMode.Script:
                            Component[] components = target.GetComponents();
                            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();
            window.titleContent = new GUIContent("Filter");
            window.Show();
        }
        //筛选的目标
        private enum FilterTarget
        {
            All, //在所有物体中筛选
            Specified, //在指定根级物体内筛选
        }
        private FilterTarget filterTarget = FilterTarget.All;
        //存储所有筛选条件
        private readonly List filterConditions = new List();
        //指定的筛选根级
        private Transform specifiedTarget;
        //存储所有组件类型
        private List components;
        //存储所有组件名称
        private List componentsNames;
        private readonly List selectedObjects = new List();
        private Vector2 scroll = Vector2.zero;

        private void OnEnable()
        {
            components = new List();
            componentsNames = new List();
            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 targetGameObjects = new List();
                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(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(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();
        }
    }
}
关注
打赏
1653184800
查看更多评论
立即登录/注册

微信扫码登录

0.0512s