This tool allows you to quickly select gameObjects behind others, with only two actions:
- Clic
- Scroll Up/Down !
Internal_PickClosestGO
.
This function returns the closest gameObject in front of the camera, at the position of the mouse.
private static void FindMethodByReflection()
{
Assembly editorAssembly = typeof(Editor).Assembly;
System.Type handleUtilityType = editorAssembly.GetType("UnityEditor.HandleUtility");
_internalPickClosestGameObject = handleUtilityType.GetMethod("Internal_PickClosestGO", BindingFlags.Static | BindingFlags.NonPublic);
}
private static GameObject PickObjectOnPos(Camera cam, int layers, Vector2 position, GameObject[] ignore, GameObject[] filter, out int materialIndex)
{
materialIndex = -1;
return (GameObject)_internalPickClosestGameObject.Invoke(null, new object[] { cam, layers, position, ignore, filter, materialIndex });
}
ExtEventEditor.cs
,
with the function we're interested in: IsClickOnSceneView();
if (_currentIndex == 0 && Selection.activeGameObject != _underneethGameObjects[0])
{
_underneethGameObjects.Insert(0, Selection.activeGameObject);
}
using hedCommon.extension.editor;
using hedCommon.extension.runtime;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace hedCommon.saveLastSelection
{
/// <summary>
/// This tool permit you to quickly select gameObjects behind others, with only two action:
/// - Clic
/// - Scroll Up/Down !
/// </summary>
[InitializeOnLoad]
public static class ClickAndScroll
{
private const int MAX_DEPTH = 40;
private static List<GameObject> _underneethGameObjects = new List<GameObject>(MAX_DEPTH);
private static MethodInfo _internalPickClosestGameObject;
private static int _currentIndex = 0;
static ClickAndScroll()
{
if (EditorApplication.isPlaying)
{
return;
}
SceneView.duringSceneGui += OnSceneGUI;
FindMethodByReflection();
_currentIndex = 0;
}
/// <summary>
/// Save reference of the Internal_PickClosestGO reflection method
/// </summary>
private static void FindMethodByReflection()
{
Assembly editorAssembly = typeof(Editor).Assembly;
System.Type handleUtilityType = editorAssembly.GetType("UnityEditor.HandleUtility");
_internalPickClosestGameObject = handleUtilityType.GetMethod("Internal_PickClosestGO", BindingFlags.Static | BindingFlags.NonPublic);
}
/// <summary>
/// pick a gameObject from the sceneView at a given mouse position
/// </summary>
/// <param name="cam">current camera</param>
/// <param name="layers">layer accepted</param>
/// <param name="position">mouse position</param>
/// <param name="ignore">ignored gameObjects</param>
/// <param name="filter"></param>
/// <param name="materialIndex"></param>
/// <returns></returns>
private static GameObject PickObjectOnPos(Camera cam, int layers, Vector2 position, GameObject[] ignore, GameObject[] filter, out int materialIndex)
{
materialIndex = -1;
return (GameObject)_internalPickClosestGameObject.Invoke(null, new object[] { cam, layers, position, ignore, filter, materialIndex });
}
/// <summary>
/// get the right click on sceneView
/// </summary>
/// <param name="sceneView"></param>
private static void OnSceneGUI(SceneView sceneView)
{
if (ExtEventEditor.IsClickOnSceneView(Event.current))
{
SaveAllGameObjectUnderneethMouse(Event.current.mousePosition, sceneView);
}
else
{
AttemptToScroll();
}
}
/// <summary>
/// attempt to create a contextMenu
/// </summary>
/// <param name="pos"></param>
/// <param name="sceneView"></param>
private static void SaveAllGameObjectUnderneethMouse(Vector2 pos, SceneView sceneView)
{
_underneethGameObjects.Clear();
Vector2 invertedPos = new Vector2(pos.x, sceneView.position.height - 16 - pos.y);
for (int i = 0; i <= MAX_DEPTH; i++)
{
GameObject clickedGameObject = PickObjectOnPos(sceneView.camera, ~0, invertedPos, _underneethGameObjects.ToArray(), null, out int matIndex);
if (clickedGameObject != null)
{
_underneethGameObjects.AddIfNotContain(clickedGameObject);
}
}
_currentIndex = 0;
}
private static void AttemptToScroll()
{
if (_underneethGameObjects.Count <= 1)
{
return;
}
bool isScrollingDown = ExtEventEditor.IsScrollingDown(Event.current, out float delta);
bool isScrollingUp = ExtEventEditor.IsScrollingUp(Event.current, out delta);
if (!isScrollingDown && !isScrollingUp)
{
return;
}
bool isShiftHeld = Event.current.shift;
bool isControlHeld = Event.current.control;
bool isSpecialKeyHeld = isShiftHeld || isControlHeld;
if (isSpecialKeyHeld && isScrollingDown)
{
if (_currentIndex == 0 && Selection.activeGameObject != _underneethGameObjects[0])
{
_underneethGameObjects.Insert(0, Selection.activeGameObject);
}
_currentIndex++;
SelectItem(isShiftHeld, isControlHeld);
ExtEventEditor.Use();
}
else if (isSpecialKeyHeld && isScrollingUp)
{
if (_currentIndex == 0 && Selection.activeGameObject != _underneethGameObjects[0])
{
_underneethGameObjects.Insert(0, Selection.activeGameObject);
}
else
{
_currentIndex--;
}
SelectItem(isShiftHeld, isControlHeld);
ExtEventEditor.Use();
}
}
private static void SelectItem(bool isShiftHeld, bool isControlHeld)
{
_currentIndex = SetBetween(_currentIndex, 0, _underneethGameObjects.Count - 1);
if (isControlHeld)
{
Selection.activeGameObject = _underneethGameObjects[_currentIndex];
}
if (isShiftHeld)
{
EditorGUIUtility.PingObject(_underneethGameObjects[_currentIndex]);
}
}
private static int SetBetween(int currentValue, int value1, int value2)
{
if (value1 > value2)
{
Debug.LogError("value2 can be less than value1");
return (0);
}
if (currentValue < value1)
{
currentValue = value1;
}
if (currentValue > value2)
{
currentValue = value2;
}
return (currentValue);
}
//end of class
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace hedCommon.extension.editor
{
public static class ExtEventEditor
{
private static bool _wasMouseDown;
private static Vector2 _clickDownPosition;
public enum Modifier
{
NONE = 0,
CONTROL = 10,
SHIFT = 20,
ALT = 30,
}
public enum ButtonType
{
NONE = 0,
LEFT = 10,
RIGHT = 20,
MIDDLE = 30,
}
public static void Use()
{
if (Event.current.type != EventType.Layout && Event.current.type != EventType.Repaint)
{
Event.current.Use();
}
}
private static List<Modifier> SetupModifiers(Event current)
{
List<Modifier> modifiers = new List<Modifier>();
if (current.control)
{
modifiers.Add(Modifier.CONTROL);
}
if (current.alt)
{
modifiers.Add(Modifier.ALT);
}
if (current.shift)
{
modifiers.Add(Modifier.SHIFT);
}
return (modifiers);
}
/// <summary>
/// warning: current.button return 0 if nothing is pressed !! don't use alone
/// </summary>
/// <param name="current"></param>
/// <returns></returns>
private static ButtonType GetButtonType(Event current)
{
if (current.button == 0)
{
return (ButtonType.LEFT);
}
else if (current.button == 1)
{
return (ButtonType.RIGHT);
}
else if (current.button == 2)
{
return (ButtonType.MIDDLE);
}
return (ButtonType.NONE);
}
public static bool IsScrolling(Event current, out Vector2 delta)
{
delta = Vector2.zero;
int controlId = GUIUtility.GetControlID(FocusType.Passive);
EventType eventType = current.GetTypeForControl(controlId);
if (eventType == EventType.ScrollWheel)
{
delta = current.delta;
return (true);
}
return (false);
}
public static bool IsScrollingUp(Event current, out float delta)
{
delta = 0;
int controlId = GUIUtility.GetControlID(FocusType.Passive);
EventType eventType = current.GetTypeForControl(controlId);
if (eventType == EventType.ScrollWheel)
{
delta = current.delta.y;
return (delta > 0);
}
return (false);
}
public static bool IsScrollingDown(Event current, out float delta)
{
delta = 0;
int controlId = GUIUtility.GetControlID(FocusType.Passive);
EventType eventType = current.GetTypeForControl(controlId);
if (eventType == EventType.ScrollWheel)
{
delta = current.delta.y;
return (delta < 0);
}
return (false);
}
public static bool IsLeftMouseUp(Event current)
{
bool clicked = IsMouseClicked(current, out EventType eventType, out List<Modifier> modifiers, out ButtonType buttonType);
return (clicked && eventType == EventType.MouseUp && buttonType == ButtonType.LEFT);
}
public static bool IsLeftMouseDown(Event current)
{
bool clicked = IsMouseClicked(current, out EventType eventType, out List<Modifier> modifiers, out ButtonType buttonType);
return (clicked && eventType == EventType.MouseDown && buttonType == ButtonType.LEFT);
}
public static bool IsRightMouseUp(Event current, bool returnTrueIfMarkedAsUsed = false)
{
bool clicked = IsMouseClicked(current, out EventType eventType, out List<Modifier> modifiers, out ButtonType buttonType);
if (returnTrueIfMarkedAsUsed && eventType == EventType.Used && buttonType == ButtonType.RIGHT)
{
return (true);
}
return (clicked && eventType == EventType.MouseUp && buttonType == ButtonType.RIGHT);
}
public static bool IsRightMouseDown(Event current, bool returnTrueIfMarkedAsUsed = false)
{
bool clicked = IsMouseClicked(current, out EventType eventType, out List<Modifier> modifiers, out ButtonType buttonType);
if (returnTrueIfMarkedAsUsed && eventType == EventType.Used && buttonType == ButtonType.RIGHT)
{
return (true);
}
return (clicked && eventType == EventType.MouseDown && buttonType == ButtonType.RIGHT);
}
public static bool IsMiddleMouseUp(Event current)
{
bool clicked = IsMouseClicked(current, out EventType eventType, out List<Modifier> modifiers, out ButtonType buttonType);
return (clicked && eventType == EventType.MouseUp && buttonType == ButtonType.MIDDLE);
}
public static bool IsMiddleMouseDown(Event current)
{
bool clicked = IsMouseClicked(current, out EventType eventType, out List<Modifier> modifiers, out ButtonType buttonType);
return (clicked && eventType == EventType.MouseDown && buttonType == ButtonType.MIDDLE);
}
public static bool IsKeyUp(Event current, KeyCode keyCode)
{
if (current == null)
{
return (false);
}
int controlId = GUIUtility.GetControlID(FocusType.Passive);
EventType eventType = current.GetTypeForControl(controlId);
bool pressed = current.keyCode == keyCode && eventType == EventType.KeyUp;
return (pressed);
}
public static bool IsKeyDown(KeyCode keyCode)
{
if (Event.current == null)
{
return (false);
}
int controlId = GUIUtility.GetControlID(FocusType.Passive);
EventType eventType = Event.current.GetTypeForControl(controlId);
bool pressed = Event.current.keyCode == keyCode && eventType == EventType.KeyDown;
return (Event.current.keyCode == keyCode);
}
/// <summary>
/// return true if the left mouse button is Down
/// </summary>
/// <param name="current">Event.current (only in a OnGUI() loop)</param>
/// <returns>true if mouse is Down</returns>
public static bool IsMouseClicked(Event current, out EventType eventType, out List<Modifier> modifiers, out ButtonType buttonType)
{
int controlId = GUIUtility.GetControlID(FocusType.Passive);
modifiers = new List<Modifier>();
eventType = current.GetTypeForControl(controlId);
buttonType = GetButtonType(current);
if (eventType == EventType.MouseUp)
{
eventType = EventType.MouseUp;
modifiers = SetupModifiers(current);
return (true);
}
if (eventType == EventType.MouseDown)
{
eventType = EventType.MouseDown;
modifiers = SetupModifiers(current);
return (true);
}
return (false);
}
/// <summary>
/// DOESN'T WORK IN ONE SHOOT, need to be called at least twice,
/// once on mouseDown, and once on mouseUp
/// </summary>
/// <param name="current"></param>
/// <returns></returns>
public static bool IsClickOnSceneView(Event current)
{
if (ExtEventEditor.IsLeftMouseDown(current))
{
_clickDownPosition = current.mousePosition;
_wasMouseDown = true;
return (false);
}
if (!_wasMouseDown)
{
return (false);
}
if (current.type == EventType.Used && current.mousePosition == _clickDownPosition && current.delta == Vector2.zero)
{
_wasMouseDown = false;
return (true);
}
return (false);
}
}
}