Unity 编辑器开发实战【Scene View】- UI Selector

举报
CoderZ1010 发表于 2022/09/25 06:29:16 2022/09/25
【摘要】         在Scene窗口编辑UI界面时,当重叠的UI元素较多时,很难点选想要选中的元素,UI Selector工具做了如下功能:右键时弹出一个列表,列举所有包含鼠标当前位置的RectTransform物体,在列表中选择即可选中该UI元素。 &n...

        在Scene窗口编辑UI界面时,当重叠的UI元素较多时,很难点选想要选中的元素,UI Selector工具做了如下功能:右键时弹出一个列表,列举所有包含鼠标当前位置的RectTransform物体,在列表中选择即可选中该UI元素。

        实现该功能需要使用Scene View类,本人用的Unity版本是2020.3.16,该版本里显示onSceneGUIDelegate是弃用状态,使用duringSceneGui代替:


  
  1. using UnityEngine;
  2. using UnityEditor;
  3. namespace SK.Framework
  4. {
  5. [InitializeOnLoad]
  6. public static class UISelector
  7. {
  8. static UISelector()
  9. {
  10. SceneView.duringSceneGui += OnSceneGUI;
  11. }
  12. private static void OnSceneGUI(SceneView sceneView)
  13. {
  14. }
  15. }
  16. }

        注意使用InitializeOnLoad属性,该属性应用的对象是静态构造函数,它可以保证在编辑器启动的时候调用该构造函数,因此我们在构造函数中使用SceneView类中的duringSceneGui来实现Scene窗口的自定义功能。

        首先我们想要在鼠标右键点击时弹出列表,在编辑器环境中的输入使用Event类,下面的代码表示鼠标右键抬起:


  
  1. var ec = Event.current;
  2. if (ec != null && ec.button == 1 && ec.type == EventType.MouseUp)
  3. {
  4. }

        列表中列举所有包含当前鼠标位置的Rect Transform,所以要先获取当前加载的场景中的所有Rect Transform组件:


  
  1. private static void OnSceneGUI(SceneView sceneView)
  2. {
  3. var ec = Event.current;
  4. if (ec != null && ec.button == 1 && ec.type == EventType.MouseUp)
  5. {
  6. ec.Use();
  7. var scenes = GetAllScenes();
  8. var groups = scenes
  9. .Where(m => m.isLoaded)
  10. .SelectMany(m => m.GetRootGameObjects())
  11. .Where(m => m.activeInHierarchy)
  12. .SelectMany(m => m.GetComponentsInChildren<RectTransform>())
  13. .GroupBy(m => m.gameObject.scene.name)
  14. .ToArray();
  15. }
  16. }
  17. private static IEnumerable<Scene> GetAllScenes()
  18. {
  19. for (int i = 0; i < SceneManager.sceneCount; i++)
  20. {
  21. yield return SceneManager.GetSceneAt(i);
  22. }
  23. }

        获取到所有的RectTransform组件后,判断哪些包含当前鼠标位置,通过Event.current中的mousePosition可以获得当前鼠标位置,但是需要注意,该坐标系中的原点为左上角:

        而UGUI中Canvas的坐标系以左下角为原点,因此需要先进行坐标转换,然后再通过Rect Transform Utility类中的RectangleContainsScreenPoint函数可以判断RectTransform是否包含指定位置:


  
  1. using UnityEngine;
  2. using UnityEditor;
  3. using System.Linq;
  4. using System.Collections.Generic;
  5. using UnityEngine.SceneManagement;
  6. namespace SK.Framework
  7. {
  8. [InitializeOnLoad]
  9. public static class UISelector
  10. {
  11. static UISelector()
  12. {
  13. SceneView.duringSceneGui += OnSceneGUI;
  14. }
  15. private static void OnSceneGUI(SceneView sceneView)
  16. {
  17. var ec = Event.current;
  18. if (ec != null && ec.button == 1 && ec.type == EventType.MouseUp)
  19. {
  20. ec.Use();
  21. // 当前屏幕坐标,左上角是(0,0)右下角(camera.pixelWidth,camera.pixelHeight)
  22. Vector2 mousePosition = Event.current.mousePosition;
  23. // Retina 屏幕需要拉伸值
  24. float mult = EditorGUIUtility.pixelsPerPoint;
  25. // 转换成摄像机可接受的屏幕坐标,左下角是(0,0,0)右上角是(camera.pixelWidth,camera.pixelHeight,0)
  26. mousePosition.y = sceneView.camera.pixelHeight - mousePosition.y * mult;
  27. mousePosition.x *= mult;
  28. var scenes = GetAllScenes();
  29. var groups = scenes
  30. .Where(m => m.isLoaded)
  31. .SelectMany(m => m.GetRootGameObjects())
  32. .Where(m => m.activeInHierarchy)
  33. .SelectMany(m => m.GetComponentsInChildren<RectTransform>())
  34. .Where(m => RectTransformUtility.RectangleContainsScreenPoint(m, mousePosition, sceneView.camera))
  35. .GroupBy(m => m.gameObject.scene.name)
  36. .ToArray();
  37. }
  38. }
  39. private static IEnumerable<Scene> GetAllScenes()
  40. {
  41. for (int i = 0; i < SceneManager.sceneCount; i++)
  42. {
  43. yield return SceneManager.GetSceneAt(i);
  44. }
  45. }
  46. }
  47. }

同时还要处理同名UI元素问题,以及当前加载的场景可能不止一个的情况,如下:

        最终通过GenericMenu类实现右键菜单,通过Selection类中activeTransform和EditorGUI Utility类中PingObject实现选中,完整代码:


  
  1. using UnityEngine;
  2. using UnityEditor;
  3. using System.Linq;
  4. using System.Collections.Generic;
  5. using UnityEngine.SceneManagement;
  6. namespace SK.Framework
  7. {
  8. [InitializeOnLoad]
  9. public static class UISelector
  10. {
  11. static UISelector()
  12. {
  13. SceneView.duringSceneGui += OnSceneGUI;
  14. }
  15. private static void OnSceneGUI(SceneView sceneView)
  16. {
  17. var ec = Event.current;
  18. if (ec != null && ec.button == 1 && ec.type == EventType.MouseUp)
  19. {
  20. ec.Use();
  21. // 当前屏幕坐标,左上角是(0,0)右下角(camera.pixelWidth,camera.pixelHeight)
  22. Vector2 mousePosition = Event.current.mousePosition;
  23. // Retina 屏幕需要拉伸值
  24. float mult = EditorGUIUtility.pixelsPerPoint;
  25. // 转换成摄像机可接受的屏幕坐标,左下角是(0,0,0)右上角是(camera.pixelWidth,camera.pixelHeight,0)
  26. mousePosition.y = sceneView.camera.pixelHeight - mousePosition.y * mult;
  27. mousePosition.x *= mult;
  28. var scenes = GetAllScenes();
  29. var groups = scenes
  30. .Where(m => m.isLoaded)
  31. .SelectMany(m => m.GetRootGameObjects())
  32. .Where(m => m.activeInHierarchy)
  33. .SelectMany(m => m.GetComponentsInChildren<RectTransform>())
  34. .Where(m => RectTransformUtility.RectangleContainsScreenPoint(m, mousePosition, sceneView.camera))
  35. .GroupBy(m => m.gameObject.scene.name)
  36. .ToArray();
  37. var sceneCount = scenes.Count(m => m.isLoaded);
  38. var gc = new GenericMenu();
  39. var dic = new Dictionary<string, int>();
  40. foreach (var group in groups)
  41. {
  42. foreach (var rt in group)
  43. {
  44. var name = rt.name;
  45. var sceneName = rt.gameObject.scene.name;
  46. var nameWithSceneName = sceneName + "/" + name;
  47. var isContains = dic.ContainsKey(nameWithSceneName);
  48. var text = sceneCount <= 1 ? name : nameWithSceneName;
  49. if (isContains)
  50. {
  51. var count = dic[nameWithSceneName]++;
  52. text += " [" + count.ToString() + "]";
  53. }
  54. var content = new GUIContent(text);
  55. gc.AddItem(content, false, () =>
  56. {
  57. Selection.activeTransform = rt;
  58. EditorGUIUtility.PingObject(rt.gameObject);
  59. });
  60. if (!isContains)
  61. {
  62. dic.Add(nameWithSceneName, 1);
  63. }
  64. }
  65. }
  66. gc.ShowAsContext();
  67. }
  68. }
  69. private static IEnumerable<Scene> GetAllScenes()
  70. {
  71. for (int i = 0; i < SceneManager.sceneCount; i++)
  72. {
  73. yield return SceneManager.GetSceneAt(i);
  74. }
  75. }
  76. }
  77. }

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

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

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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