using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; #if UNITY_5_3_OR_NEWER using UnityEngine.SceneManagement; #endif namespace Plugins.Isolationist.Editor { [InitializeOnLoad] public static class EditorIsolateCommand { private const string ISOLATE_KEY_PREF = "IsolationistKey"; private const string ISOLATE_ALT_PREF = "IsolationistAlt"; private const string ISOLATE_CTRL_PREF = "IsolationistCtrl"; private const string ISOLATE_SHIFT_PREF = "IsolationistShift"; private const string ISOLATE_HIDE_LIGHTS_PREF = "IsolationistHideLights"; private const string ISOLATE_HIDE_CAMERAS_PREF = "IsolationistHideCameras"; private static bool _alt; private static bool _ctrl; private static bool _shift; private static bool _ctrlOrShiftPressed; private static bool _hideLights; private static bool _hideCameras; private static KeyCode _hotkey; private static GameObject _lastSelection; private static int _lastSelectionCount; private static string _shortcutDisplay; private static List _lastSelectionList; private static readonly List _lightTypeList = new List(); private static readonly List _cameraTypeList = new List(); public static void AddLightType(Type type) { _lightTypeList.Add(type); } public static void AddCameraType(Type type) { _cameraTypeList.Add(type); } static EditorIsolateCommand() { _alt = EditorPrefs.GetBool(ISOLATE_ALT_PREF, false); _ctrl = EditorPrefs.GetBool(ISOLATE_CTRL_PREF, false); _shift = EditorPrefs.GetBool(ISOLATE_SHIFT_PREF, false); _hotkey = (KeyCode) EditorPrefs.GetInt(ISOLATE_KEY_PREF, (int) KeyCode.I); _hideLights = EditorPrefs.GetBool(ISOLATE_HIDE_LIGHTS_PREF, true); _hideCameras = EditorPrefs.GetBool(ISOLATE_HIDE_CAMERAS_PREF, true); EditorApplication.update += Update; EditorApplication.playmodeStateChanged += PlaymodeStateChanged; EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowItemOnGUI; SceneView.onSceneGUIDelegate += OnSceneGUI; AddCameraType(typeof(Camera)); AddLightType(typeof(Light)); AddLightType(typeof(ReflectionProbe)); } private static bool IsolateKeyPressed { get { if (Event.current == null) return false; if (Event.current.type != EventType.KeyUp) return false; return Event.current.keyCode == _hotkey && Event.current.alt == _alt && Event.current.control == _ctrl && Event.current.shift == _shift; } } public static string ShortcutDisplay { get { return _shortcutDisplay.IsNullOrEmpty() ? _shortcutDisplay = GetShortcutDisplay() : _shortcutDisplay; } } private static void Update() { if (!IsolateInfo.IsIsolated || _lastSelection == Selection.activeGameObject && _lastSelectionCount == Selection.gameObjects.Length) return; var selectionList = Selection.gameObjects.ToList(); var newItems = _lastSelectionList == null ? selectionList : selectionList.Except(_lastSelectionList).ToList(); _lastSelection = Selection.activeGameObject; _lastSelectionCount = Selection.gameObjects.Length; _lastSelectionList = selectionList; SelectionChanged(newItems); } private static void OnSceneGUI(SceneView sceneView) { OnGUI(); } private static void HierarchyWindowItemOnGUI(int instanceId, Rect selectionRect) { if (!GUI.GetNameOfFocusedControl().IsNullOrEmpty()) return; OnGUI(); } private static void OnGUI() { _ctrlOrShiftPressed = Event.current.control || Event.current.shift; if (!IsolateKeyPressed) return; ToggleIsolate(); Event.current.Use(); } private static void PlaymodeStateChanged() { if (EditorApplication.isPlayingOrWillChangePlaymode) IsolateInfo.Show(); else IsolateInfo.Hide(); } private static void SelectionChanged(List newItems) { if (WasHidden(Selection.activeTransform) && !_ctrlOrShiftPressed) { EndIsolation(); return; } if (!_ctrlOrShiftPressed) return; UpdateIsolation(newItems); } private static List GetAllGameObjectsToHide() { return IsolateInfo.Instance.FocusObjects.SelectMany(GetGameObjectsToHide).Distinct().ToList(); } [MenuItem("Tools/Toggle Isolate", true)] public static bool CanToggleIsolate() { return Selection.activeGameObject || IsolateInfo.IsIsolated; } [MenuItem("Tools/Toggle Isolate")] public static void ToggleIsolate() { if (IsolateInfo.IsIsolated) EndIsolation(); else StartIsolation(); } private static void StartIsolation() { if (IsolateInfo.Instance) { Debug.LogWarning("Isolationist: Found previous isolation info. This shouldn't happen. Ending the previous isolation anyway."); EndIsolation(); } if (EditorApplication.isPlayingOrWillChangePlaymode) { Debug.LogWarning("Isolationist: Can't isolate while playing. It'll break stuff!"); return; } // Create new IsolateInfo object. var container = new GameObject("IsolationInfo") {hideFlags = HideFlags.HideInHierarchy}; Undo.RegisterCreatedObjectUndo(container, "Isolate"); IsolateInfo.Instance = container.AddComponent(); var focusList = IsolateInfo.Instance.FocusObjects = Selection.gameObjects.ToList(); if (!_hideLights) _lightTypeList.ForEach(t => focusList.AddRange(Object.FindObjectsOfType(t).Select(ObjectToGO))); if (!_hideCameras) _cameraTypeList.ForEach(t => focusList.AddRange(Object.FindObjectsOfType(t).Select(ObjectToGO))); IsolateInfo.Instance.HiddenObjects = GetAllGameObjectsToHide(); if (!IsolateInfo.Instance.HiddenObjects.Any()) { Object.DestroyImmediate(container); Debug.LogWarning("Isolationist: Nothing to isolate."); return; } Undo.RecordObjects(IsolateInfo.Instance.HiddenObjects.Cast().ToArray(), "Isolate"); IsolateInfo.Hide(); } private static GameObject ObjectToGO(Object obj) { Component component = obj as Component; return component ? component.gameObject : null; } private static void UpdateIsolation(List newItems) { if (!newItems.Any()) return; Undo.RecordObject(IsolateInfo.Instance, "Isolate"); Undo.RecordObjects(IsolateInfo.Instance.HiddenObjects.Cast().ToArray(), "Isolate"); IsolateInfo.Show(); IsolateInfo.Instance.FocusObjects = IsolateInfo.Instance.FocusObjects.Concat(newItems).Distinct().ToList(); var newHiddenObjects = GetAllGameObjectsToHide(); Undo.RecordObjects(newHiddenObjects.Except(IsolateInfo.Instance.HiddenObjects).Cast().ToArray(), "Isolate"); IsolateInfo.Instance.HiddenObjects = newHiddenObjects; IsolateInfo.Hide(); } private static bool WasHidden(Transform t) { return t && !t.gameObject.activeInHierarchy && !t.GetComponent() && !IsolateInfo.Instance.FocusObjects.Any(t.gameObject.IsRelative); } private static bool CanHide(Transform t) { return t && t.gameObject.activeSelf && !t.GetComponent() && !IsolateInfo.Instance.FocusObjects.Any(t.gameObject.IsRelative); } private static IEnumerable GetGameObjectsToHide(GameObject keeperGo) { if (!keeperGo) return new List(); var keeper = keeperGo.transform; var transformsToHide = new List(); while (keeper.parent) { transformsToHide.AddRange(keeper.parent.GetChildren().Where(CanHide)); keeper = keeper.parent; } transformsToHide.AddRange(GetRootTransforms().Where(CanHide)); return transformsToHide.Select(t => t.gameObject); } [PreferenceItem("Isolationist")] public static void PreferencesGUI() { GUILayout.Label("Shortcut: " + ShortcutDisplay); _ctrl = EditorGUILayout.Toggle("Ctrl", _ctrl); _alt = EditorGUILayout.Toggle("Alt", _alt); _shift = EditorGUILayout.Toggle("Shift", _shift); _hotkey = (KeyCode) EditorGUILayout.EnumPopup("Shortcut Key", _hotkey); GUILayout.Label(""); _hideLights = EditorGUILayout.Toggle("Hide Lights", _hideLights); _hideCameras = EditorGUILayout.Toggle("Hide Cameras", _hideCameras); if (!GUI.changed) return; EditorPrefs.SetBool(ISOLATE_CTRL_PREF, _ctrl); EditorPrefs.SetBool(ISOLATE_ALT_PREF, _alt); EditorPrefs.SetBool(ISOLATE_SHIFT_PREF, _shift); EditorPrefs.SetBool(ISOLATE_HIDE_LIGHTS_PREF, _hideLights); EditorPrefs.SetBool(ISOLATE_HIDE_CAMERAS_PREF, _hideCameras); EditorPrefs.SetInt(ISOLATE_KEY_PREF, (int) _hotkey); _shortcutDisplay = GetShortcutDisplay(); } private static void EndIsolation() { if (!IsolateInfo.Instance) return; if (IsolateInfo.Instance.HiddenObjects != null) { Undo.RecordObjects(IsolateInfo.Instance.HiddenObjects.Cast().ToArray(), "DeIsolate"); IsolateInfo.Show(); } Undo.DestroyObjectImmediate(IsolateInfo.Instance.gameObject); } private static string GetShortcutDisplay() { var display = ""; if (_ctrl) display += "Ctrl+"; if (_alt) display += "Alt+"; if (_shift) display += "Shift+"; display += _hotkey; return display; } #region Utils private static bool IsParent(this Transform parent, Transform transform) { while (parent) { if (parent == transform) return true; parent = parent.parent; } return false; } private static bool IsParent(this GameObject parent, GameObject go) { return parent && go && IsParent(parent.transform, go.transform); } private static bool IsRelative(this GameObject go1, GameObject go2) { return go2.IsParent(go1) || go1.IsParent(go2); } private static IEnumerable GetChildren(this Transform t) { var children = new List(); for (var i = 0; i < t.childCount; i++) children.Add(t.GetChild(i)); return children; } private static IEnumerable GetRootSceneObjects() { #if UNITY_5_3_OR_NEWER return SceneManager.GetActiveScene().GetRootGameObjects(); #else var prop = new HierarchyProperty(HierarchyType.GameObjects); var expanded = new int[0]; while (prop.Next(expanded)) yield return prop.pptrValue as GameObject; #endif } private static IEnumerable GetRootTransforms() { return GetRootSceneObjects().Where(go => go).Select(go => go.transform); } private static bool IsNullOrEmpty(this string str) { return string.IsNullOrEmpty(str); } #endregion } }