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


  
  1. using UnityEngine;
  2. using UnityEditor;
  3. using UnityEngine.SceneManagement;
  4. using System;
  5. using System.Reflection;
  6. using System.Collections.Generic;
  7. namespace SK.Framework
  8. {
  9. /// <summary>
  10. /// 过滤类型
  11. /// </summary>
  12. public enum FilterMode
  13. {
  14. Name, //根据名字筛选
  15. Component, //根据组件筛选
  16. Layer, //根据层级筛选
  17. Tag, //根据标签筛选
  18. Active, //根据是否活跃筛选
  19. Missing, //丢失筛选
  20. }
  21. public enum MissingMode
  22. {
  23. Material, //材质丢失
  24. Mesh, //网格丢失
  25. Script //脚本丢失
  26. }
  27. [SerializeField]
  28. public class FilterCondition
  29. {
  30. public FilterMode filterMode;
  31. public MissingMode missingMode;
  32. public string stringValue;
  33. public int intValue;
  34. public bool boolValue;
  35. public Type typeValue;
  36. public FilterCondition(FilterMode filterMode, string stringValue)
  37. {
  38. this.filterMode = filterMode;
  39. this.stringValue = stringValue;
  40. }
  41. public FilterCondition(FilterMode filterMode, int intValue)
  42. {
  43. this.filterMode = filterMode;
  44. this.intValue = intValue;
  45. }
  46. public FilterCondition(FilterMode filterMode, bool boolValue)
  47. {
  48. this.filterMode = filterMode;
  49. this.boolValue = boolValue;
  50. }
  51. public FilterCondition(FilterMode filterMode, Type typeValue)
  52. {
  53. this.filterMode = filterMode;
  54. this.typeValue = typeValue;
  55. }
  56. public FilterCondition(FilterMode filterMode, MissingMode missingMode)
  57. {
  58. this.filterMode = filterMode;
  59. this.missingMode = missingMode;
  60. }
  61. /// <summary>
  62. /// 判断物体是否符合条件
  63. /// </summary>
  64. /// <param name="target">物体</param>
  65. /// <returns>符合条件返回true,否则返回false</returns>
  66. public bool IsMatch(GameObject target)
  67. {
  68. switch (filterMode)
  69. {
  70. case FilterMode.Name: return target.name.ToLower().Contains(stringValue.ToLower());
  71. case FilterMode.Component: return target.GetComponent(typeValue) != null;
  72. case FilterMode.Layer: return target.layer == intValue;
  73. case FilterMode.Tag: return target.CompareTag(stringValue);
  74. case FilterMode.Active: return target.activeSelf == boolValue;
  75. case FilterMode.Missing:
  76. switch (missingMode)
  77. {
  78. case MissingMode.Material:
  79. var mr = target.GetComponent<MeshRenderer>();
  80. if (mr == null) return false;
  81. Material[] materials = mr.sharedMaterials;
  82. bool flag = false;
  83. for (int i = 0; i < materials.Length; i++)
  84. {
  85. if(materials[i] == null)
  86. {
  87. flag = true;
  88. break;
  89. }
  90. }
  91. return flag;
  92. case MissingMode.Mesh:
  93. var mf = target.GetComponent<MeshFilter>();
  94. if (mf == null) return false;
  95. return mf.sharedMesh == null;
  96. case MissingMode.Script:
  97. Component[] components = target.GetComponents<Component>();
  98. bool retV = false;
  99. for (int i = 0; i < components.Length; i++)
  100. {
  101. if(components[i] == null)
  102. {
  103. retV = true;
  104. break;
  105. }
  106. }
  107. return retV;
  108. default:
  109. return false;
  110. }
  111. default: return false;
  112. }
  113. }
  114. }
  115. public sealed class Filter : EditorWindow
  116. {
  117. [MenuItem("SKFramework/Tools/Filter")]
  118. private static void Open()
  119. {
  120. var window = GetWindow<Filter>();
  121. window.titleContent = new GUIContent("Filter");
  122. window.Show();
  123. }
  124. //筛选的目标
  125. private enum FilterTarget
  126. {
  127. All, //在所有物体中筛选
  128. Specified, //在指定根级物体内筛选
  129. }
  130. private FilterTarget filterTarget = FilterTarget.All;
  131. //存储所有筛选条件
  132. private readonly List<FilterCondition> filterConditions = new List<FilterCondition>();
  133. //指定的筛选根级
  134. private Transform specifiedTarget;
  135. //存储所有组件类型
  136. private List<Type> components;
  137. //存储所有组件名称
  138. private List<string> componentsNames;
  139. private readonly List<GameObject> selectedObjects = new List<GameObject>();
  140. private Vector2 scroll = Vector2.zero;
  141. private void OnEnable()
  142. {
  143. components = new List<Type>();
  144. componentsNames = new List<string>();
  145. Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
  146. for (int i = 0; i < assemblies.Length; i++)
  147. {
  148. var types = assemblies[i].GetTypes();
  149. for (int j = 0; j < types.Length; j++)
  150. {
  151. if (types[j].IsSubclassOf(typeof(Component)))
  152. {
  153. components.Add(types[j]);
  154. componentsNames.Add(types[j].Name);
  155. }
  156. }
  157. }
  158. }
  159. private void OnGUI()
  160. {
  161. OnTargetGUI();
  162. scroll = EditorGUILayout.BeginScrollView(scroll);
  163. OnConditionGUI();
  164. OnIsMatchedGameObjectsGUI();
  165. EditorGUILayout.EndScrollView();
  166. OnFilterGUI();
  167. }
  168. private void OnTargetGUI()
  169. {
  170. filterTarget = (FilterTarget)EditorGUILayout.EnumPopup("Target", filterTarget);
  171. switch (filterTarget)
  172. {
  173. case FilterTarget.Specified:
  174. specifiedTarget = EditorGUILayout.ObjectField("Root", specifiedTarget, typeof(Transform), true) as Transform;
  175. break;
  176. }
  177. EditorGUILayout.Space();
  178. }
  179. private void OnConditionGUI()
  180. {
  181. if (GUILayout.Button("Create New Condition", "DropDownButton"))
  182. {
  183. GenericMenu gm = new GenericMenu();
  184. gm.AddItem(new GUIContent("Name"), false, () => filterConditions.Add(new FilterCondition(FilterMode.Name, "GameObject")));
  185. gm.AddItem(new GUIContent("Component"), false, () => filterConditions.Add(new FilterCondition(FilterMode.Component, typeof(Transform))));
  186. gm.AddItem(new GUIContent("Layer"), false, () => filterConditions.Add(new FilterCondition(FilterMode.Layer, 0)));
  187. gm.AddItem(new GUIContent("Tag"), false, () => filterConditions.Add(new FilterCondition(FilterMode.Tag, "Untagged")));
  188. gm.AddItem(new GUIContent("Active"), false, () => filterConditions.Add(new FilterCondition(FilterMode.Active, true)));
  189. gm.AddItem(new GUIContent("Missing / Material"), false, () => filterConditions.Add(new FilterCondition(FilterMode.Missing, MissingMode.Material)));
  190. gm.AddItem(new GUIContent("Missing / Mesh"), false, () => filterConditions.Add(new FilterCondition(FilterMode.Missing, MissingMode.Mesh)));
  191. gm.AddItem(new GUIContent("Missing / Script"), false, () => filterConditions.Add(new FilterCondition(FilterMode.Missing, MissingMode.Script)));
  192. gm.ShowAsContext();
  193. }
  194. EditorGUILayout.Space();
  195. if(filterConditions.Count > 0)
  196. {
  197. GUILayout.BeginVertical("Badge");
  198. for (int i = 0; i < filterConditions.Count; i++)
  199. {
  200. var condition = filterConditions[i];
  201. GUILayout.BeginHorizontal();
  202. if(filterConditions.Count > 1)
  203. GUILayout.Label($"{i + 1}.", GUILayout.Width(30f));
  204. switch (condition.filterMode)
  205. {
  206. case FilterMode.Name:
  207. GUILayout.Label("Name", GUILayout.Width(80f));
  208. condition.stringValue = EditorGUILayout.TextField(condition.stringValue);
  209. break;
  210. case FilterMode.Component:
  211. var index = componentsNames.FindIndex(m => m == condition.typeValue.Name);
  212. GUILayout.Label("Component", GUILayout.Width(80f));
  213. var newIndex = EditorGUILayout.Popup(index, componentsNames.ToArray());
  214. if (index != newIndex) condition.typeValue = components[newIndex];
  215. break;
  216. case FilterMode.Layer:
  217. GUILayout.Label("Layer", GUILayout.Width(80f));
  218. condition.intValue = EditorGUILayout.LayerField(condition.intValue);
  219. break;
  220. case FilterMode.Tag:
  221. GUILayout.Label("Tag", GUILayout.Width(80f));
  222. condition.stringValue = EditorGUILayout.TagField(condition.stringValue);
  223. break;
  224. case FilterMode.Active:
  225. GUILayout.Label("Active", GUILayout.Width(80f));
  226. condition.boolValue = EditorGUILayout.Toggle(condition.boolValue);
  227. break;
  228. case FilterMode.Missing:
  229. GUILayout.Label("Missing", GUILayout.Width(80f));
  230. condition.missingMode = (MissingMode)EditorGUILayout.EnumPopup(condition.missingMode);
  231. break;
  232. default:
  233. break;
  234. }
  235. if (GUILayout.Button("×", "MiniButton", GUILayout.Width(20f)))
  236. {
  237. filterConditions.RemoveAt(i);
  238. return;
  239. }
  240. GUILayout.EndHorizontal();
  241. }
  242. GUILayout.EndVertical();
  243. }
  244. EditorGUILayout.Space();
  245. }
  246. private void OnIsMatchedGameObjectsGUI()
  247. {
  248. for (int i = 0; i < selectedObjects.Count; i++)
  249. {
  250. GameObject obj = selectedObjects[i];
  251. if(obj == null)
  252. {
  253. selectedObjects.RemoveAt(i);
  254. i--;
  255. continue;
  256. }
  257. GUILayout.BeginHorizontal("IN Title");
  258. GUILayout.Label(obj.name);
  259. GUILayout.EndHorizontal();
  260. if (Event.current.type == EventType.MouseDown && GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition))
  261. {
  262. EditorGUIUtility.PingObject(obj);
  263. }
  264. }
  265. GUILayout.FlexibleSpace();
  266. }
  267. private void OnFilterGUI()
  268. {
  269. GUILayout.BeginHorizontal();
  270. if (GUILayout.Button("Filter", "ButtonLeft"))
  271. {
  272. selectedObjects.Clear();
  273. List<GameObject> targetGameObjects = new List<GameObject>();
  274. switch (filterTarget)
  275. {
  276. case FilterTarget.All:
  277. GameObject[] rootGameObjects = SceneManager.GetActiveScene().GetRootGameObjects();
  278. for (int i = 0; i < rootGameObjects.Length; i++)
  279. {
  280. var root = rootGameObjects[i];
  281. var allChildren = root.GetComponentsInChildren<Transform>(true);
  282. for (int j = 0; j < allChildren.Length; j++)
  283. {
  284. EditorUtility.DisplayProgressBar("Filter", allChildren[j].name, (float)i / rootGameObjects.Length);
  285. targetGameObjects.Add(allChildren[j].gameObject);
  286. }
  287. }
  288. EditorUtility.ClearProgressBar();
  289. break;
  290. case FilterTarget.Specified:
  291. Transform[] children = specifiedTarget.GetComponentsInChildren<Transform>(true);
  292. for (int i = 0; i < children.Length; i++)
  293. {
  294. EditorUtility.DisplayProgressBar("Filter", children[i].name, (float)i / children.Length);
  295. targetGameObjects.Add(children[i].gameObject);
  296. }
  297. EditorUtility.ClearProgressBar();
  298. break;
  299. default:
  300. break;
  301. }
  302. for (int i = 0; i < targetGameObjects.Count; i++)
  303. {
  304. GameObject target = targetGameObjects[i];
  305. bool isMatch = true;
  306. for (int j = 0; j < filterConditions.Count; j++)
  307. {
  308. if (!filterConditions[j].IsMatch(target))
  309. {
  310. isMatch = false;
  311. break;
  312. }
  313. }
  314. EditorUtility.DisplayProgressBar("Filter", $"{target.name} -> Is Matched : {isMatch}", (float)i / targetGameObjects.Count);
  315. if (isMatch)
  316. {
  317. selectedObjects.Add(target);
  318. }
  319. }
  320. EditorUtility.ClearProgressBar();
  321. }
  322. if (GUILayout.Button("Select", "ButtonMid"))
  323. {
  324. Selection.objects = selectedObjects.ToArray();
  325. }
  326. if (GUILayout.Button("Clear", "ButtonRight"))
  327. {
  328. selectedObjects.Clear();
  329. }
  330. GUILayout.EndHorizontal();
  331. }
  332. }
  333. }

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

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

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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