Last active
June 5, 2019 15:54
-
-
Save dimmduh/bbff81ee6ae0eb4179c5c661d1c1363a to your computer and use it in GitHub Desktop.
Put somewhere in Editor folder
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Linq; | |
using UnityEditor; | |
using UnityEngine; | |
using Object = UnityEngine.Object; | |
//version 0.2 | |
[InitializeOnLoad] | |
public static class EditorHelperHotkeys | |
{ | |
// private const double doublePressMaxDelay = 0.25; | |
//////////////////// SETTINGS //////////// | |
private static string newGroupNameFormat = "Group {0}"; | |
private static int selectionUndoLimit = 20; | |
//////////////////// /SETTINGS //////////// | |
/// | |
private static int nextGroupId = 1; | |
private static Object[] lastSelection; | |
private static MaxStack<Object[]> selectionUndo = new MaxStack<Object[]>(selectionUndoLimit); | |
private static bool justUndo; | |
private static bool justPush; | |
// private static double prevPressTime; | |
// private static KeyCode prevPressKeyCode = KeyCode.None; | |
static EditorHelperHotkeys() | |
{ | |
if (SceneView.onSceneGUIDelegate != null) | |
{ | |
SceneView.onSceneGUIDelegate -= OnSceneGUI; | |
} | |
SceneView.onSceneGUIDelegate += OnSceneGUI; | |
if (Selection.selectionChanged != null && Selection.selectionChanged.GetInvocationList().Contains(new Action(OnSelectionChanged))) | |
Selection.selectionChanged -= OnSelectionChanged; | |
Selection.selectionChanged += OnSelectionChanged; | |
} | |
private static void OnSelectionChanged() | |
{ | |
//just canceled | |
if (justUndo) | |
{ | |
justUndo = false; | |
return; | |
} | |
if (Selection.objects.Length == 0) | |
{ | |
justPush = false; | |
return; | |
} | |
if (lastSelection == Selection.objects) | |
return; | |
selectionUndo.Push(Selection.objects); | |
lastSelection = Selection.objects; | |
justPush = true; | |
Debug.Log("Add = " + selectionUndo.Count); | |
} | |
[MenuItem("Edit/Undo Only Selection %#z")] | |
private static void UndoSelectionChanged() | |
{ | |
if (selectionUndo.Count == 0) | |
return; | |
if (justPush) | |
{ | |
selectionUndo.Pop(); | |
justPush = false; | |
} | |
if (selectionUndo.Count > 0) | |
{ | |
justUndo = true; | |
if (selectionUndo.Count > 1) | |
Selection.objects = selectionUndo.Pop(); | |
else | |
Selection.objects = selectionUndo.Peek(); | |
} | |
lastSelection = selectionUndo.Count == 0 ? null : selectionUndo.Peek(); | |
Debug.Log("Undo = " + selectionUndo.Count); | |
} | |
private static void OnSceneGUI(SceneView sceneView) | |
{ | |
OnUpdate(); | |
} | |
private static void OnUpdate() | |
{ | |
var e = Event.current; | |
if (e == null) | |
return; | |
if (!e.control || e.alt || e.type != EventType.KeyDown) | |
return; | |
// var diff = EditorApplication.timeSinceStartup - prevPressTime; | |
// prevPressTime = EditorApplication.timeSinceStartup; | |
// prevPressKeyCode = e.keyCode; | |
var applyGlobal = e.shift; | |
switch (e.type) | |
{ | |
case EventType.KeyDown: | |
switch (e.keyCode) | |
{ | |
case KeyCode.W: | |
ResetPosition(applyGlobal); | |
Event.current.Use(); | |
break; | |
case KeyCode.E: | |
ResetRotation(applyGlobal); | |
Event.current.Use(); | |
break; | |
case KeyCode.R: | |
ResetScale(applyGlobal); | |
// Event.current.Use(); | |
break; | |
case KeyCode.T: | |
ResetTransform(applyGlobal); | |
Event.current.Use(); | |
break; | |
} | |
break; | |
} | |
} | |
/* | |
private static void ResetKeys() | |
{ | |
prevPressTime = 0; | |
prevPressKeyCode = KeyCode.None; | |
} | |
*/ | |
[MenuItem("Edit/Group %g")] | |
private static void GroupObjects() | |
{ | |
if (Selection.gameObjects.Length <= 1) | |
return; | |
var firstTransformInGroup = Selection.gameObjects[0].transform; | |
var sibling = firstTransformInGroup.GetSiblingIndex(); | |
//detect is RectTransform or Transform | |
var useRectTransform = Selection.gameObjects.Any(go => go.transform is RectTransform); | |
//create new object | |
var name = string.Format(newGroupNameFormat, nextGroupId); | |
var groupTransform = new GameObject(name, useRectTransform ? typeof(RectTransform) : typeof(Transform)).transform; | |
nextGroupId++; | |
groupTransform.SetParent(firstTransformInGroup.parent); | |
groupTransform.SetSiblingIndex(sibling + 1); | |
Undo.RegisterCreatedObjectUndo(groupTransform.gameObject, "Group gameObjects"); | |
foreach (var go in Selection.gameObjects) | |
{ | |
Undo.SetTransformParent(go.transform, groupTransform, "Group gameObjects"); | |
} | |
SetExpandedRecursive(groupTransform.gameObject, true); | |
Selection.activeObject = groupTransform.gameObject; | |
} | |
[MenuItem("Edit/UnParent %u")] | |
private static void UnParentObjects() | |
{ | |
foreach (var go in Selection.gameObjects) | |
{ | |
if (go.transform.parent != null) | |
Undo.SetTransformParent(go.transform, null, "UnParent gameObjects"); | |
} | |
} | |
private static void ResetPosition(bool global) | |
{ | |
foreach (var go in Selection.gameObjects) | |
{ | |
Undo.RecordObject(go.transform, "Reset position"); | |
if (global) | |
go.transform.position = Vector3.zero; | |
else | |
go.transform.localPosition = Vector3.zero; | |
} | |
} | |
private static void ResetRotation(bool global) | |
{ | |
foreach (var go in Selection.gameObjects) | |
{ | |
Undo.RecordObject(go.transform, "Reset rotation"); | |
if (global) | |
go.transform.rotation = Quaternion.identity; | |
else | |
go.transform.localRotation = Quaternion.identity; | |
} | |
} | |
private static void ResetScale(bool global) | |
{ | |
foreach (var go in Selection.gameObjects) | |
{ | |
Undo.RecordObject(go.transform, "Reset scale"); | |
go.transform.localScale = Vector3.one; | |
if (global) | |
{ | |
var lossyScale = go.transform.lossyScale; | |
go.transform.localScale = new Vector3(1 / lossyScale.x, 1 / lossyScale.y, 1 / lossyScale.z); | |
} | |
} | |
} | |
private static void ResetTransform(bool global) | |
{ | |
foreach (var go in Selection.gameObjects) | |
{ | |
Undo.RecordObject(go.transform, "Reset transform"); | |
go.transform.localScale = Vector3.one; | |
if (global) | |
{ | |
go.transform.position = Vector3.zero; | |
go.transform.rotation = Quaternion.identity; | |
} | |
else | |
{ | |
go.transform.localPosition = Vector3.zero; | |
go.transform.localRotation = Quaternion.identity; | |
var lossyScale = go.transform.lossyScale; | |
go.transform.localScale = new Vector3(1 / lossyScale.x, 1 / lossyScale.y, 1 / lossyScale.z); | |
} | |
} | |
} | |
///////////////// UTILS //////////// | |
public static void SetExpandedRecursive(GameObject go, bool expand) | |
{ | |
var type = typeof(EditorWindow).Assembly.GetType("UnityEditor.SceneHierarchyWindow"); | |
var methodInfo = type.GetMethod("SetExpandedRecursive"); | |
try | |
{ | |
EditorApplication.ExecuteMenuItem("Window/General/Hierarchy"); | |
var window = EditorWindow.focusedWindow; | |
methodInfo.Invoke(window, new object[] {go.GetInstanceID(), expand}); | |
} | |
catch (Exception) | |
{ | |
// ignored | |
} | |
} | |
} | |
/// <summary> | |
/// Generic stack implementation with a maximum limit | |
/// When something is pushed on the last item is removed from the list | |
/// @see http://ntsblog.homedev.com.au/index.php/2010/05/06/c-stack-with-maximum-limit/ | |
/// </summary> | |
[Serializable] | |
public class MaxStack<T> | |
{ | |
#region Fields | |
private int _limit; | |
private LinkedList<T> _list; | |
#endregion | |
#region Constructors | |
public MaxStack(int maxSize) | |
{ | |
_limit = maxSize; | |
_list = new LinkedList<T>(); | |
} | |
#endregion | |
#region Public Stack Implementation | |
public void Push(T value) | |
{ | |
if (_list.Count == _limit) | |
{ | |
_list.RemoveLast(); | |
} | |
_list.AddFirst(value); | |
} | |
public T Pop() | |
{ | |
if (_list.Count > 0) | |
{ | |
T value = _list.First.Value; | |
_list.RemoveFirst(); | |
return value; | |
} | |
else | |
{ | |
throw new InvalidOperationException("The Stack is empty"); | |
} | |
} | |
public T Peek() | |
{ | |
if (_list.Count > 0) | |
{ | |
T value = _list.First.Value; | |
return value; | |
} | |
else | |
{ | |
throw new InvalidOperationException("The Stack is empty"); | |
} | |
} | |
public void Clear() | |
{ | |
_list.Clear(); | |
} | |
public int Count | |
{ | |
get { return _list.Count; } | |
} | |
/// <summary> | |
/// Checks if the top object on the stack matches the value passed in | |
/// </summary> | |
/// <param name="value"></param> | |
/// <returns></returns> | |
public bool IsTop(T value) | |
{ | |
bool result = false; | |
if (this.Count > 0) | |
{ | |
result = Peek().Equals(value); | |
} | |
return result; | |
} | |
public bool Contains(T value) | |
{ | |
bool result = false; | |
if (this.Count > 0) | |
{ | |
result = _list.Contains(value); | |
} | |
return result; | |
} | |
public IEnumerator GetEnumerator() | |
{ | |
return _list.GetEnumerator(); | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment