using UnityEngine; using UnityEditor; using UnityEngine.UI; using System.Collections.Generic; using System.Linq; [InitializeOnLoad] public class QuickSelector//原生菜单 { // 配置键名 private const string PREF_UI = "QS_EnableUI"; private const string PREF_3D = "QS_Enable3D"; private const string PREF_REVERSE = "QS_ReverseOrder"; static QuickSelector() { SceneView.duringSceneGui += OnSceneGUI; } private static void OnSceneGUI(SceneView sceneView) { Event e = Event.current; // Shift + 右键触发 if (e.type == EventType.MouseDown && e.button == 1 && e.shift) { // 加载配置 bool enableUI = EditorPrefs.GetBool(PREF_UI, true); bool enable3D = EditorPrefs.GetBool(PREF_3D, true); bool reverseOrder = EditorPrefs.GetBool(PREF_REVERSE, false); Vector2 guiPos = e.mousePosition; guiPos.y = sceneView.camera.pixelHeight - guiPos.y; Ray ray = HandleUtility.GUIPointToWorldRay(e.mousePosition); HashSet processedObjects = new HashSet(); List entries = new List(); // 1. 智能拾取 (PickGameObject) GameObject smartPick = HandleUtility.PickGameObject(e.mousePosition, false); if (smartPick != null) AddEntry(entries, processedObjects, smartPick, "PICK"); // 2. UI 扫描 if (enableUI) { var allRects = GameObject.FindObjectsByType(FindObjectsSortMode.None); foreach (var rect in allRects) { if (!rect.gameObject.activeInHierarchy) continue; if (RectTransformUtility.RectangleContainsScreenPoint(rect, guiPos, sceneView.camera)) AddEntry(entries, processedObjects, rect.gameObject, "UI"); } } // 3. 3D/Mesh 扫描 if (enable3D) { RaycastHit[] hits = Physics.RaycastAll(ray, float.MaxValue); foreach (var hit in hits) AddEntry(entries, processedObjects, hit.collider.gameObject, "3D"); Renderer[] allRenderers = GameObject.FindObjectsByType(FindObjectsSortMode.None); foreach (var r in allRenderers) { if (processedObjects.Contains(r.gameObject)) continue; if (r.bounds.IntersectRay(ray, out _)) AddEntry(entries, processedObjects, r.gameObject, "Mesh"); } } if (entries.Count > 0 || true) { GenericMenu menu = new GenericMenu(); // 根节点分组:UI组优先 var groups = entries.GroupBy(x => x.rootObject) .OrderByDescending(g => g.Any(ent => ent.majorType == "UI")) .ThenBy(g => g.Key.name); foreach (var group in groups) { if (menu.GetItemCount() > 0) menu.AddSeparator(""); // 组内排序 var sortedInGroup = reverseOrder ? group.OrderByDescending(x => x.depth).ThenByDescending(x => x.siblingIndex) : group.OrderBy(x => x.depth).ThenBy(x => x.siblingIndex); int minDepth = group.Min(x => x.depth); int maxDepth = group.Max(x => x.depth); foreach (var entry in sortedInGroup) { // --- 修复点:缩进计算 --- // 顺序:深度越深缩进越多 (entry.depth - minDepth) // 逆序:深度越浅缩进越多 (maxDepth - entry.depth) int indentLevel = reverseOrder ? (maxDepth - entry.depth) : (entry.depth - minDepth); string indent = new string(' ', indentLevel * 2); string label = $"{indent}[ {entry.majorType} | {entry.minorType} ]: {entry.gameObject.name}"; menu.AddItem(new GUIContent(label), false, () => { Selection.activeGameObject = entry.gameObject; EditorGUIUtility.PingObject(entry.gameObject); }); } } // 设置项 menu.AddSeparator(""); menu.AddItem(new GUIContent("Settings/Detect UI"), enableUI, () => EditorPrefs.SetBool(PREF_UI, !enableUI)); menu.AddItem(new GUIContent("Settings/Detect 3D"), enable3D, () => EditorPrefs.SetBool(PREF_3D, !enable3D)); menu.AddItem(new GUIContent("Settings/Reverse Order"), reverseOrder, () => EditorPrefs.SetBool(PREF_REVERSE, !reverseOrder)); menu.ShowAsContext(); e.Use(); } } } private static void AddEntry(List list, HashSet set, GameObject go, string source) { if (go == null || !set.Add(go)) return; string major = "3D"; string minor = "Object"; if (source == "UI" || go.GetComponent() != null) { major = "UI"; minor = GetUIComponentType(go); } else if (go.GetComponent() != null) { major = "3D"; minor = "Collider"; } else if (go.GetComponent() != null) { major = "Mesh"; minor = go.GetComponent().GetType().Name; } list.Add(new SelectionEntry { gameObject = go, rootObject = GetRoot(go.transform), depth = GetDepth(go.transform), siblingIndex = go.transform.GetSiblingIndex(), majorType = major, minorType = minor }); } private static string GetUIComponentType(GameObject go) { if (go.GetComponent("TextMeshProUGUI")) return "TMPro"; if (go.GetComponent()) return "Text"; if (go.GetComponent