首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

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

  • 25-03-03 17:01
  • 4031
  • 12427
blog.csdn.net

    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. ///
  10. /// 过滤类型
  11. ///
  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. ///
  62. /// 判断物体是否符合条件
  63. ///
  64. /// 物体
  65. /// 符合条件返回true,否则返回false
  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();
  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();
  94. if (mf == null) return false;
  95. return mf.sharedMesh == null;
  96. case MissingMode.Script:
  97. Component[] components = target.GetComponents();
  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();
  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 filterConditions = new List();
  133. //指定的筛选根级
  134. private Transform specifiedTarget;
  135. //存储所有组件类型
  136. private List components;
  137. //存储所有组件名称
  138. private List<string> componentsNames;
  139. private readonly List selectedObjects = new List();
  140. private Vector2 scroll = Vector2.zero;
  141. private void OnEnable()
  142. {
  143. components = new List();
  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 targetGameObjects = new List();
  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(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(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. }
当代野生程序猿
微信公众号
Unity开发日志分享,欢迎关注/留言/私信。
注:本文转载自blog.csdn.net的CoderZ1010的文章"https://blog.csdn.net/qq_42139931/article/details/119643874"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top