using UnityEngine; using UnityEditor; using UnityEngine.UI; using System.Collections.Generic; using System.Linq; [InitializeOnLoad] public class QuickSelectorHud { public static GameObject HoveredObject; static QuickSelectorHud() { SceneView.duringSceneGui += OnSceneGUI; } private static void OnSceneGUI(SceneView sceneView) { if (HoveredObject != null) { DrawHighlight(HoveredObject); sceneView.Repaint(); } Event e = Event.current; if (e.type == EventType.MouseDown && e.button == 1 && e.shift) { var entries = CollectData(sceneView, e.mousePosition); QuickSelectorPopup popup = new QuickSelectorPopup(entries); PopupWindow.Show(new Rect(e.mousePosition.x, e.mousePosition.y, 0, 0), popup); e.Use(); } } private static void DrawHighlight(GameObject go) { Handles.color = new Color(0f, 0.7f, 1f, 1f); // 亮蓝色 RectTransform rt = go.GetComponent(); if (rt != null) { Vector3[] corners = new Vector3[4]; rt.GetWorldCorners(corners); Handles.DrawPolyLine(corners[0], corners[1], corners[2], corners[3], corners[0]); Handles.color = new Color(0f, 0.7f, 1f, 0.1f); Handles.DrawAAConvexPolygon(corners); } else { Renderer r = go.GetComponent(); if (r != null) Handles.DrawWireCube(r.bounds.center, r.bounds.size); } } private static List CollectData(SceneView sceneView, Vector2 mousePos) { bool enableUI = EditorPrefs.GetBool("QS_UI", true); bool enable3D = EditorPrefs.GetBool("QS_3D", true); Vector2 guiPos = mousePos; guiPos.y = sceneView.camera.pixelHeight - guiPos.y; Ray ray = HandleUtility.GUIPointToWorldRay(mousePos); HashSet processed = new HashSet(); List list = new List(); GameObject smart = HandleUtility.PickGameObject(mousePos, false); if (smart != null) AddEntry(list, processed, smart, "PICK"); if (enableUI) { var rects = GameObject.FindObjectsByType(FindObjectsSortMode.None); foreach (var rect in rects) { if (rect.gameObject.activeInHierarchy && RectTransformUtility.RectangleContainsScreenPoint(rect, guiPos, sceneView.camera)) AddEntry(list, processed, rect.gameObject, "UI"); } } if (enable3D) { RaycastHit[] hits = Physics.RaycastAll(ray, float.MaxValue); foreach (var hit in hits) AddEntry(list, processed, hit.collider.gameObject, "3D"); foreach (var r in GameObject.FindObjectsByType(FindObjectsSortMode.None)) if (!processed.Contains(r.gameObject) && r.bounds.IntersectRay(ray, out _)) AddEntry(list, processed, r.gameObject, "Mesh"); } return list; } private static void AddEntry(List list, HashSet set, GameObject go, string src) { if (go == null || !set.Add(go)) return; string maj = (go.GetComponent() != null) ? "UI" : (go.GetComponent() != null ? "3D" : "Mesh"); string min = (maj == "UI") ? GetUIType(go) : (go.GetComponent()?.GetType().Name ?? "Object"); list.Add(new SelectionEntry { go = go, root = GetRoot(go.transform), depth = GetDepth(go.transform), sibling = go.transform.GetSiblingIndex(), major = maj, minor = min }); } private static string GetUIType(GameObject go) { if (go.GetComponent("TextMeshProUGUI")) return "TMPro"; if (go.GetComponent()) return "Text"; if (go.GetComponent()) return "Image"; return "Rect"; } private static GameObject GetRoot(Transform t) { while (t.parent != null) t = t.parent; return t.gameObject; } private static int GetDepth(Transform t) { int d = 0; while (t.parent != null) { d++; t = t.parent; } return d; } } public class SelectionEntry { public GameObject go, root; public int depth, sibling; public string major, minor; } public class QuickSelectorPopup : PopupWindowContent { private List _entries; private List _displayItems; private Vector2 _scroll; private GUIStyle _hoverStyle; private GUIStyle _richLabelStyle; // 修正点:手动创建支持富文本的 Style public QuickSelectorPopup(List entries) { _entries = entries; RefreshList(); } public override Vector2 GetWindowSize() => new Vector2(280, Mathf.Min((_displayItems.Count * 22) + 30, 450)); public override void OnGUI(Rect rect) { // 样式初始化 if (_hoverStyle == null) { _hoverStyle = new GUIStyle(EditorStyles.label); _hoverStyle.normal.background = MakeTex(2, 2, new Color(0.2f, 0.5f, 1f, 0.4f)); } if (_richLabelStyle == null) { _richLabelStyle = new GUIStyle(EditorStyles.label); _richLabelStyle.richText = true; // 关键修正:开启富文本支持 } _scroll = EditorGUILayout.BeginScrollView(_scroll); Event e = Event.current; foreach (var item in _displayItems) { if (item.isSep) { EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); continue; } Rect r = EditorGUILayout.GetControlRect(false, 20); bool isHover = r.Contains(e.mousePosition); if (isHover) { GUI.Box(r, "", _hoverStyle); if (QuickSelectorHud.HoveredObject != item.ent.go) { QuickSelectorHud.HoveredObject = item.ent.go; EditorGUIUtility.PingObject(item.ent.go); SceneView.RepaintAll(); } if (e.type == EventType.MouseDown && e.button == 0) { Selection.activeGameObject = item.ent.go; this.editorWindow.Close(); } } // 使用修正后的富文本样式绘制 GUI.Label(r, item.label, _richLabelStyle); } EditorGUILayout.EndScrollView(); DrawToolbar(); if (e.type == EventType.MouseMove && !rect.Contains(e.mousePosition)) { QuickSelectorHud.HoveredObject = null; } } private void DrawToolbar() { GUILayout.BeginHorizontal(EditorStyles.toolbar); if (GUILayout.Toggle(EditorPrefs.GetBool("QS_UI", true), "UI", EditorStyles.toolbarButton) != EditorPrefs.GetBool("QS_UI", true)) { EditorPrefs.SetBool("QS_UI", !EditorPrefs.GetBool("QS_UI")); RefreshList(); } if (GUILayout.Toggle(EditorPrefs.GetBool("QS_3D", true), "3D", EditorStyles.toolbarButton) != EditorPrefs.GetBool("QS_3D", true)) { EditorPrefs.SetBool("QS_3D", !EditorPrefs.GetBool("QS_3D")); RefreshList(); } GUILayout.FlexibleSpace(); if (GUILayout.Toggle(EditorPrefs.GetBool("QS_REV", false), "Reverse", EditorStyles.toolbarButton) != EditorPrefs.GetBool("QS_REV", false)) { EditorPrefs.SetBool("QS_REV", !EditorPrefs.GetBool("QS_REV")); RefreshList(); } GUILayout.EndHorizontal(); } public override void OnClose() { QuickSelectorHud.HoveredObject = null; SceneView.RepaintAll(); } private void RefreshList() { _displayItems = new List(); bool rev = EditorPrefs.GetBool("QS_REV", false); var groups = _entries.GroupBy(x => x.root).OrderByDescending(g => g.Any(e => e.major == "UI")).ThenBy(g => g.Key.name); bool first = true; foreach (var g in groups) { if (!first) _displayItems.Add(new DisplayItem { isSep = true }); first = false; var sorted = rev ? g.OrderByDescending(x => x.depth).ThenByDescending(x => x.sibling) : g.OrderBy(x => x.depth).ThenBy(x => x.sibling); int minD = g.Min(x => x.depth); int maxD = g.Max(x => x.depth); foreach (var ent in sorted) { int ind = rev ? (maxD - ent.depth) : (ent.depth - minD); string color = (ent.major == "UI" ? "#00E6FF" : "#AAAAAA"); // 调整 UI 标签为更亮的青色 string label = $"{new string(' ', ind * 4)}[{ent.major}|{ent.minor}] {ent.go.name}"; _displayItems.Add(new DisplayItem { ent = ent, label = label }); } } if (editorWindow != null) editorWindow.Repaint(); } private Texture2D MakeTex(int w, int h, Color col) { Color[] pix = new Color[w * h]; for (int i = 0; i < pix.Length; i++) pix[i] = col; Texture2D t = new Texture2D(w, h); t.SetPixels(pix); t.Apply(); return t; } private class DisplayItem { public bool isSep; public SelectionEntry ent; public string label; } }