#if UNITY_EDITOR using System; using MonKey.Editor.Commands; using MonKey.Extensions; using MonKey.Settings.Internal; using System.Collections.Generic; using System.Linq; using UnityEditor; #if UNITY_2022_1_OR_NEWER using UnityEditor.SceneManagement; #else #endif using UnityEngine; using Object = UnityEngine.Object; public class MonKeySelectionUtils { public static readonly List SelectionSave = new List(); public static readonly List PreviousSelectionStack = new List(); public static readonly List SelectionStack = new List(); public static GameObject LastGOSelected; public static int CurrentSelectionCount = 0; public static List OrderedObjects => SelectionStack; public static List OrderedGameObjects { get { return SelectionStack.Where(_ => _ is GameObject) .Convert(_ => _ as GameObject).ToList(); } } public static List OrderedTransforms { get { return SelectionStack.Where(_ => _ is GameObject) .Convert(_ => _ as GameObject).Where(_ => _.scene.IsValid()).Convert(_ => _.transform).ToList(); } } [InitializeOnLoadMethod] public static void PluginImporter() { SelectionStack.AddRange(Selection.objects); PreviousSelectionStack.Clear(); PreviousSelectionStack.AddRange(Selection.objects); Selection.selectionChanged -= UpdateOrderedSelection2; Selection.selectionChanged += UpdateOrderedSelection2; } public static void UpdateOrderedSelection() { if (!InitializeSelectionUpdate()) return; if (Selection.objects.Length < OrderedObjects.Count) { //then one objects wha removed using ctrl click HandleObjectRemoval(); } else if (Selection.objects.Length == OrderedObjects.Count + 1) { //then one object was added with click HandleOneObjectAdded(); } else { //then several objects were added with shift+click HandleSeveralObjectsAdded(); } } private static void HandleSeveralObjectsAdded() { /* var addedOjects = FindNewObjects(); var addedGameObject = addedOjects.Where(_ =>!IsAsset(_)).Cast(); var addedAsset = addedOjects.Where(IsAsset); var orderedGameObjects = addedGameObject.OrderBy(CalculateOrder); if()*/ } private static float CalculateOrder(GameObject obj) { float order = obj.transform.GetSiblingIndex(); int digit = 1; Transform parent = obj.transform.parent; while (parent) { order += parent.GetSiblingIndex() * digit; digit++; parent = parent.parent; } order /= (digit); return order; } private static void HandleOneObjectAdded() { var addedOjects = FindNewObjects(); Debug.Assert(addedOjects.Count == 1, "MonKey Error: something went wrong with what Unity did with the selection"); var obj = addedOjects.First(); SelectionStack.Add(obj); } private static bool IsAsset(Object obj) { var go = obj as GameObject; bool isPrefabStage = UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage() != null; if (go && (go.scene.IsValid() || (isPrefabStage && UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage().scene == go.scene))) { return false; } return true; } private static List FindNewObjects() { var newObjs = new List(); foreach (Object o in Selection.objects) { if (SelectionStack.Contains(o)) continue; newObjs.Add(o); } return newObjs; } private static bool InitializeSelectionUpdate() { EditorUtility.ClearProgressBar(); if (!MonKeyInternalSettings.Instance || !MonKeyInternalSettings.Instance.UseSortedSelection) return false; if (Selection.objects.Length > MonKeyInternalSettings.Instance.MaxSortedSelectionSize && MonKeyInternalSettings.Instance.ShowSortedSelectionWarning) { Debug.Log("MonKey Info: " + "You selected more objects than the limit set in settings for the maximum amount of ordered objects:" + " the selection's order won't be guaranteed. "); Debug.Log("...You can change that amount in the settings, but keep in mind that trying to " + "used ordered selection on large amount of objects will require some " + "calculation time."); Debug.Log("You can disable that warning in the settings as well."); SelectionStack.Clear(); SelectionStack.AddRange(Selection.objects); return false; } if (Selection.objects.Length > 50) { EditorUtility.DisplayProgressBar("MonKey Is Updating Ordered Selection, Please Wait", "Making sure the selection is properly ordered. If this is getting too long, you can deactivate that ", 0.5f); } return true; } private static void HandleObjectRemoval() { throw new NotImplementedException(); } public static void UpdateOrderedSelection2() { /* PreviousSelectionStack.Clear(); PreviousSelectionStack.AddRange(Selection.objects);*/ if (Selection.objects.Length > 0) { PreviousSelectionStack.Clear(); PreviousSelectionStack.AddRange(Selection.objects); } if (Selection.objects == null) { SelectionStack.Clear(); return; } EditorUtility.ClearProgressBar(); if (!MonKeyInternalSettings.Instance || !MonKeyInternalSettings.Instance.UseSortedSelection) return; if (Selection.objects.Length > MonKeyInternalSettings.Instance.MaxSortedSelectionSize && MonKeyInternalSettings.Instance.ShowSortedSelectionWarning) { Debug.Log("MonKey Info: " + "You selected more objects than the limit set in settings for the maximum amount of ordered objects:" + " the selection's order won't be guaranteed. "); Debug.Log("...You can change that amount in the settings, but keep in mind that trying to " + "used ordered selection on large amount of objects will require some " + "calculation time."); Debug.Log("You can disable that warning in the settings as well."); SelectionStack.Clear(); SelectionStack.AddRange(Selection.objects); return; } if (Selection.objects.Length > 50) { EditorUtility.DisplayProgressBar("MonKey Is Updating Ordered Selection, Please Wait", "Making sure the selection is properly ordered. If this is getting too long, you can deactivate that ", 0.5f); } //if there is a current selection and it has only one object now or none, //then the selection can be considered changed and we can store the previous selection if (Selection.objects.Length <= 1) { if (SelectionStack.Count >= 1) { SelectionStack.Clear(); } if (Selection.objects.IsEmpty()) { CurrentSelectionCount = 0; LastGOSelected = null; } else { SelectionStack.AddRange(Selection.objects); CurrentSelectionCount = 1; if (Selection.objects[0] is GameObject) { LastGOSelected = (GameObject) Selection.objects[0]; } } } else { //in that case we need to check the differences and adjust the ordered selection accordingly if (Selection.objects.Length <= CurrentSelectionCount) { //if so, it means that some objects were removed, //we need to check which ones and remove them for (int j = SelectionStack.Count - 1; j >= 0; j--) { if (!Selection.objects.Contains(SelectionStack[j])) { SelectionStack.RemoveAt(j); CurrentSelectionCount--; } } } else { //then some objects were added, and must be sorted and added. //We first must find the objects that were added List newGOs = new List(); List newAssets = new List(); for (int j = 0; j < Selection.objects.Length; j++) { var obj = Selection.objects[j]; if (!SelectionStack.Contains(obj)) { var go = obj as GameObject; bool isPrefabStage = UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage() != null; if (go && (go.scene.IsValid() || (isPrefabStage && UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage().scene == go.scene))) { newGOs.Add(go); } else { newAssets.Add(obj); } } } CurrentSelectionCount += newGOs.Count + newAssets.Count; newAssets.Sort(new NamingUtilities.ObjectComparer()); List orderedObjects = new List(); Dictionary> orderIDs = new Dictionary>(orderedObjects.Count); List idsOfLastSelected = new List(); //now we need to determine the order for (int i = 0; i < newGOs.Count; i++) { var obj = newGOs[i]; orderIDs.Add(obj, new List()); var parentTransform = obj.transform.parent; orderIDs[obj].Add(obj.transform.GetSiblingIndex()); if (parentTransform) { while (parentTransform) { if (orderIDs[obj].Count > 0) orderIDs[obj].Insert(0, parentTransform.GetSiblingIndex()); else { orderIDs[obj].Add(parentTransform.GetSiblingIndex()); } parentTransform = parentTransform.parent; } } } if (LastGOSelected) { var lastParentTransform = LastGOSelected.transform.parent; if (!lastParentTransform) { idsOfLastSelected.Add(LastGOSelected.transform.GetSiblingIndex()); } else { while (lastParentTransform) { idsOfLastSelected.Add(lastParentTransform.GetSiblingIndex()); lastParentTransform = lastParentTransform.parent; } } } //now that we have all the orders computed, we can order the objects for (int i = 0; i < newGOs.Count; i++) { if (orderedObjects.Count == 0) { orderedObjects.Add(newGOs[i]); } else { int insertID = -1; var newGo = newGOs[i]; var newIds = orderIDs[newGo]; for (int j = 0; j < orderedObjects.Count; j++) { var ids = orderIDs[orderedObjects[j]]; for (int k = 0; k < ids.Count; k++) { if (newIds.Count <= k) break; int newID = newIds[k]; if (ids[k] > newID) { //then we can insert before insertID = j; break; } if (ids[k] == newID) { //then we need to check further, or if there isn't left just add right after if (k == ids.Count - 1) { break; } if (newIds.Count == k + 1) { //this means it's the parent of the object insertID = j; break; } } else { // then we are after, and can break to compare to the next one break; } } if (insertID != -1) { break; } } if (insertID != -1 && insertID < orderedObjects.Count) { orderedObjects.Insert(insertID, newGOs[i]); } else { orderedObjects.Add(newGOs[i]); } } } bool top = false; for (int i = 0; i < idsOfLastSelected.Count; i++) { if (orderedObjects.Count == 0) break; var list = orderIDs[orderedObjects[0]]; if (list.Count <= i) break; if (idsOfLastSelected[i] < list[i]) { top = true; break; } if (idsOfLastSelected[i] > list[i]) { top = false; break; } } if (!top) { orderedObjects.Reverse(); } //now that we have our ordered game objects, we can get the last one , and add the assets, and be done! if (orderedObjects.Count > 0) { LastGOSelected = orderedObjects.Last(); } SelectionStack.AddRange(orderedObjects.Convert(_ => _ as Object)); SelectionStack.AddRange(newAssets); } } EditorUtility.ClearProgressBar(); } } #endif