// Use picking system to get us ordered list of all visually overlapping gameobjects in screen position from top to bottom static IEnumerable <PickingObject> GetAllOverlapping(Vector2 position) { var allOverlapping = new List <PickingObject>(); var ignoreList = new List <PickingObject>(); while (true) { PickingObject res = HandleUtility.PickObject(position, false, ignoreList, null); if (res.target == null) { break; } if (res.TryGetGameObject(out var go) && SceneVisibilityManager.instance.IsPickingDisabled(go)) { ignoreList.Add(res); continue; } // Prevent infinite loop if object cannot be ignored (this needs to be fixed so print an error) if (allOverlapping.Count > 0 && res == allOverlapping.Last()) { Debug.LogError($"GetAllOverlapping failed, could not ignore game object '{res}' when picking"); break; } allOverlapping.Add(res); ignoreList.Add(res); yield return(res); } }
public void OnGUI() { Event evt = Event.current; Handles.BeginGUI(); Vector2 mousePos = evt.mousePosition; int id = s_RectSelectionID; switch (evt.GetTypeForControl(id)) { case EventType.Layout: case EventType.MouseMove: if (!Tools.viewToolActive) { HandleUtility.AddDefaultControl(id); } //Handle the case of the drag being canceled if (m_RectSelecting && GUIUtility.hotControl != id) { CompleteRectSelection(); } break; case EventType.MouseDown: if (HandleUtility.nearestControl == id && evt.button == 0) { GUIUtility.hotControl = id; m_SelectStartPoint = mousePos; m_SelectionStart = Selection.objects; m_RectSelecting = false; } break; case EventType.MouseDrag: if (GUIUtility.hotControl == id) { if (!m_RectSelecting && (mousePos - m_SelectStartPoint).magnitude > 6f) { EditorApplication.modifierKeysChanged += SendCommandsOnModifierKeys; m_RectSelecting = true; ActiveEditorTracker.delayFlushDirtyRebuild = true; m_LastSelection = null; m_CurrentSelection = null; rectSelectionStarting(); } if (m_RectSelecting) { m_SelectMousePoint = new Vector2(Mathf.Max(mousePos.x, 0), Mathf.Max(mousePos.y, 0)); GameObject[] rectObjs = HandleUtility.PickRectObjects(EditorGUIExt.FromToRect(m_SelectStartPoint, m_SelectMousePoint)); m_CurrentSelection = rectObjs; bool setIt = false; if (m_LastSelection == null) { m_LastSelection = new Dictionary <GameObject, bool>(); setIt = true; } setIt |= m_LastSelection.Count != rectObjs.Length; if (!setIt) { Dictionary <GameObject, bool> set = new Dictionary <GameObject, bool>(rectObjs.Length); foreach (GameObject g in rectObjs) { set.Add(g, false); } foreach (GameObject g in m_LastSelection.Keys) { if (!set.ContainsKey(g)) { setIt = true; break; } } } if (setIt) { m_LastSelection = new Dictionary <GameObject, bool>(rectObjs.Length); foreach (GameObject g in rectObjs) { m_LastSelection.Add(g, false); } if (evt.shift) { UpdateSelection(m_SelectionStart, rectObjs, SelectionType.Additive, m_RectSelecting); } else if (EditorGUI.actionKey) { UpdateSelection(m_SelectionStart, rectObjs, SelectionType.Subtractive, m_RectSelecting); } else { UpdateSelection(m_SelectionStart, rectObjs, SelectionType.Normal, m_RectSelecting); } } } evt.Use(); } break; case EventType.Repaint: if (GUIUtility.hotControl == id && m_RectSelecting) { EditorStyles.selectionRect.Draw(EditorGUIExt.FromToRect(m_SelectStartPoint, m_SelectMousePoint), GUIContent.none, false, false, false, false); } break; case EventType.MouseUp: if (GUIUtility.hotControl == id && evt.button == 0) { GUIUtility.hotControl = 0; if (m_RectSelecting) { CompleteRectSelection(); evt.Use(); } else { if (evt.shift || EditorGUI.actionKey) { // For shift, we check if EXACTLY the active GO is hovered by mouse and then subtract. Otherwise additive. // For control/cmd, we check if ANY of the selected GO is hovered by mouse and then subtract. Otherwise additive. // Control/cmd takes priority over shift. var hovered = HandleUtility.PickObject(evt.mousePosition, false); var handledIt = false; // shift-click deselects only if the active GO is exactly what we clicked on if (!EditorGUI.actionKey && Selection.activeObject == hovered.target) { UpdateSelection(m_SelectionStart, hovered.target, SelectionType.Subtractive, m_RectSelecting); handledIt = true; } // ctrl-click deselects everything up to prefab root, that is already selected if (!handledIt && EditorGUI.actionKey) { var selectedObjects = Selection.objects; hovered.TryGetComponent <Transform>(out var hoveredTransform); var hoveredRoot = HandleUtility.FindSelectionBaseForPicking(hoveredTransform); var deselectList = new List <Object>(); while (hovered.target != null) { if (selectedObjects.Contains(hovered.target)) { deselectList.Add(hovered.target); } if (hovered.target == hoveredRoot) { break; } if (!hovered.TryGetParent(out var parent)) { break; } hovered = new PickingObject(parent.gameObject); } if (deselectList.Any()) { UpdateSelection(m_SelectionStart, deselectList.ToArray(), SelectionType.Subtractive, m_RectSelecting); handledIt = true; } } // we did not deselect anything, so add the new thing into selection instead if (!handledIt) { var picked = HandleUtility.PickObject(evt.mousePosition, true); UpdateSelection(m_SelectionStart, picked.target, SelectionType.Additive, m_RectSelecting); } } else // With no modifier keys, we do the "cycle through overlapped" picking logic in SceneViewPicking.cs { var picked = SceneViewPicking.PickGameObject(evt.mousePosition); UpdateSelection(m_SelectionStart, picked.target, SelectionType.Normal, m_RectSelecting); } evt.Use(); } } break; case EventType.ExecuteCommand: if (id == GUIUtility.hotControl && evt.commandName == EventCommandNames.ModifierKeysChanged) { if (evt.shift) { UpdateSelection(m_SelectionStart, m_CurrentSelection, SelectionType.Additive, m_RectSelecting); } else if (EditorGUI.actionKey) { UpdateSelection(m_SelectionStart, m_CurrentSelection, SelectionType.Subtractive, m_RectSelecting); } else { UpdateSelection(m_SelectionStart, m_CurrentSelection, SelectionType.Normal, m_RectSelecting); } evt.Use(); } break; } Handles.EndGUI(); }
public static PickingObject PickGameObject(Vector2 mousePosition) { s_RetainHashes = true; var enumerator = GetAllOverlapping(mousePosition).GetEnumerator(); if (!enumerator.MoveNext()) { return(PickingObject.Empty); } PickingObject topmost = enumerator.Current; var pickingBase = topmost.TryGetComponent(out Transform trs) ? HandleUtility.FindSelectionBaseForPicking(trs) : null; // Selection base is only interesting if it's not the topmost PickingObject selectionBase = new PickingObject(pickingBase); PickingObject first = selectionBase.target == null ? topmost : selectionBase; int topmostHash = topmost.GetHashCode(); int prefixHash = topmostHash; if (Selection.activeObject == null) { // Nothing selected // Return selection base if it exists, otherwise topmost game object s_PreviousTopmostHash = topmostHash; s_PreviousPrefixHash = prefixHash; return(first); } if (topmostHash != s_PreviousTopmostHash) { // Topmost game object changed // Return selection base if exists and is not already selected, otherwise topmost game object s_PreviousTopmostHash = topmostHash; s_PreviousPrefixHash = prefixHash; return(Selection.activeObject == selectionBase.target ? topmost : first); } s_PreviousTopmostHash = topmostHash; // Pick potential selection base before topmost game object if (Selection.activeObject == selectionBase.target) { if (prefixHash == s_PreviousPrefixHash) { return(topmost); } s_PreviousPrefixHash = prefixHash; return(selectionBase); } s_ActiveObjectFilter.Clear(); s_ActiveObjectFilter.Add((PickingObject)Selection.activeObject); // Check if active game object will appear in selection stack PickingObject picked = HandleUtility.PickObject(mousePosition, false, null, s_ActiveObjectFilter); if (picked == ((PickingObject)Selection.activeObject)) { // Advance enumerator to active game object while (enumerator.Current != ((PickingObject)Selection.activeObject)) { if (!enumerator.MoveNext()) { s_PreviousPrefixHash = topmostHash; return(first); // Should not occur } UpdateHash(ref prefixHash, enumerator.Current); } } if (prefixHash != s_PreviousPrefixHash) { // Prefix hash changed, start over s_PreviousPrefixHash = topmostHash; return(first); } // Move on to next game object if (!enumerator.MoveNext()) { s_PreviousPrefixHash = topmostHash; return(first); // End reached, start over } UpdateHash(ref prefixHash, enumerator.Current); if (enumerator.Current == selectionBase) { // Skip selection base if (!enumerator.MoveNext()) { s_PreviousPrefixHash = topmostHash; return(first); // End reached, start over } UpdateHash(ref prefixHash, enumerator.Current); } s_PreviousPrefixHash = prefixHash; return(enumerator.Current); }
static void UpdateHash(ref int hash, PickingObject obj) { hash = unchecked (hash * 33 + obj.GetHashCode()); }