#if UNITY_EDITOR using MonKey.Commands; using MonKey.Editor.Internal; using MonKey.Extensions; using System; using System.Collections.Generic; using System.Linq; using UnityEditor; #if UNITY_2022_1_OR_NEWER using UnityEditor.SceneManagement; #else #endif using UnityEditorInternal; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using GameObject = UnityEngine.GameObject; using Object = UnityEngine.Object; using Random = UnityEngine.Random; namespace MonKey.Editor.Commands { public static class CreationUtilities { [Command("New Siblings", Help = "Creates new siblings for the selected game objects", QuickName = "NS", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, Category = "GameObject")] public static void CreateNewSibling( [CommandParameter(Help = "The amount of siblings to create")] int siblingAmount = 1) { if (!Selection.activeTransform) return; Undo.IncrementCurrentGroup(); Undo.SetCurrentGroupName("Create Siblings"); int group = Undo.GetCurrentGroup(); foreach (var gameObject in Selection.gameObjects) { for (int i = 0; i < siblingAmount; i++) { GameObject go = new GameObject(gameObject.name); go.transform.SetParent(gameObject.transform.parent); Undo.RegisterCreatedObjectUndo(go, "sibling created"); go.transform.position = gameObject.transform.position; } } Undo.CollapseUndoOperations(group); } [Command("New Children", Help = "Creates new children for the selected game objects", QuickName = "NC", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, Category = "GameObject")] public static void CreateNewChildren( [CommandParameter(Help = "The amount of children to create")] int amount = 1) { if (!Selection.activeTransform) return; Undo.IncrementCurrentGroup(); Undo.SetCurrentGroupName("Create Children"); int group = Undo.GetCurrentGroup(); List childrenCreated = new List(); foreach (var gameObject in Selection.gameObjects) { for (int i = 0; i < amount; i++) { GameObject go = new GameObject(gameObject.name); go.transform.SetParent(gameObject.transform); Undo.RegisterCreatedObjectUndo(go, "Children created"); go.transform.position = gameObject.transform.position; childrenCreated.Add(go); } } Selection.objects = childrenCreated.ToArray(); Undo.CollapseUndoOperations(group); } [Command("New Parent", Help = "Creates a parent object for the selection", AlwaysShow = true, QuickName = "NP", Order = -6, DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, MenuItemLinkTypeOwner = "MonkeyMenuItems", MenuItemLink = "CreateParentForSelection", Category = "GameObject")] public static void ParentSelection( [CommandParameter("The name of the new parent object")] string parentName = "New Parent", bool centerParent = false) { if (Selection.gameObjects.Length == 0) return; int group = MonkeyEditorUtils.CreateUndoGroup("Create Parent"); Vector3 center = Vector3.zero; int i = 0; GameObject[] selected = Selection.gameObjects.Where(_ => _.scene.IsValid() && _.hideFlags == HideFlags.None) .ToArray(); foreach (GameObject o in selected) { Undo.RecordObject(o.transform, "Recording transform"); center += o.transform.position; i++; } center /= i; GameObject parentObject = new GameObject(parentName); SceneManager.MoveGameObjectToScene(parentObject, Selection.gameObjects[0].scene); Undo.RegisterCreatedObjectUndo(parentObject, "Parent Object Creation"); foreach (GameObject obj in selected) { if (obj.GetComponent()) { parentObject.AddComponent(); break; } } Transform common = ParentingUtilities.FindEarliestCommonParent(selected); Transform commonParent = common != null && selected.Contains(common.gameObject) ? common.parent : common; int siblingIndex = 0; if (common == null) { GameObject first = Selection.gameObjects[0]; if (!MonkeyEditorUtils.OrderedSelectedGameObjects.Any()) first = MonkeyEditorUtils.OrderedSelectedGameObjects.First(); int j = 0; foreach (GameObject o in first.scene.GetRootGameObjects()) { if (selected.Contains(o)) { siblingIndex = j; break; } j++; } } else if (selected.Contains(common.gameObject)) siblingIndex = common.GetSiblingIndex(); if (centerParent) parentObject.transform.SetParent(commonParent); else parentObject.transform.SetParent(commonParent, false); parentObject.transform.SetSiblingIndex(siblingIndex); if (centerParent) parentObject.transform.position = center; List siblingIndexes = new List(); foreach (GameObject obj in selected) { siblingIndexes.Add(obj.transform.GetSiblingIndex()); Undo.SetTransformParent(obj.transform, parentObject.transform, "Re - Parenting"); } for (var index = 0; index < selected.Length; index++) { GameObject obj = selected[index]; obj.transform.SetSiblingIndex(siblingIndexes[index]); } Selection.objects = new Object[] { parentObject }; Selection.activeObject = parentObject; parentObject.transform.SetSiblingIndex(siblingIndexes.OrderBy(_ => _).First()); Undo.CollapseUndoOperations(group); } [Command("New Parent For Each", "Creates a new parent for each of the selected objects", DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM, QuickName = "NPE", MenuItemLink = "NewParentForEach", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "GameObject")] public static void CreateParentForEach() { int undoId = MonkeyEditorUtils.CreateUndoGroup("Parent For Each"); List createdParents = new List(); foreach (var gameObject in Selection.gameObjects.Where(_ => _.scene.IsValid())) { GameObject go = new GameObject(gameObject.name + " Parent"); createdParents.Add(go); go.transform.SetParent(gameObject.transform.parent); go.transform.Reset(); Undo.RegisterCreatedObjectUndo(go, "created new parent"); Undo.SetTransformParent(gameObject.transform, go.transform, "ReParenting"); go.transform.CopyLocal(gameObject.transform); Undo.RecordObject(gameObject.transform, "Resetting transform"); gameObject.transform.Reset(); } Selection.objects = createdParents.Convert(_ => (Object)_).ToArray(); Undo.CollapseUndoOperations(undoId); } [Command("New Instances", "Instantiates the specified prefabs under the selected objects " + "(by default one of each prefab selected)", QuickName = "NI", MenuItemLink = "NewInstance", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "GameObject")] public static void InstantiateAsChildren( [CommandParameter(ForceAutoCompleteUsage = true, PreventDefaultValueUsage = true, DefaultValueNameOverride = "No Prefab Selected", Help = "the prefabs you want to instantiate", AutoCompleteMethodName = "PrefabAssets")] string[] prefabs, [CommandParameter(Help = "The amount of instances of each prefabs to create")] int amount = 1) { GameObject[] parents = Selection.gameObjects.Where(_ => _.scene.IsValid()).ToArray(); int undoId = MonkeyEditorUtils.CreateUndoGroup("Instantiate Prefabs"); if (parents.Length == 0) { #if UNITY_2019_1_OR_NEWER var stage = UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage(); if (stage != null) { parents = new[] { stage.prefabContentsRoot }; } else { parents = new GameObject[] { null }; } #else parents = new GameObject[] { null }; #endif } if (prefabs == null || prefabs.Length == 0) { Debug.LogWarning("Monkey Warning: the prefab could not be " + "instantiated because none were selected or could be recognized"); return; } List createdObjects = new List(); int j = 0; int k = 0; Object[] selected = prefabs.Convert(_ => AssetDatabase.LoadAssetAtPath(_)).ToArray(); foreach (Object o in selected) { foreach (var parent in parents) { if (o != parent && PrefabUtility.GetPrefabAssetType(o) != PrefabAssetType.NotAPrefab || PrefabUtility.GetPrefabInstanceStatus(o) != PrefabInstanceStatus.NotAPrefab) { for (int i = 0; i < amount; i++) { if (EditorUtility.DisplayCancelableProgressBar("Instantiating Prefabs", "Instantiating " + o.name, ((float)i + i * j + i * j * k) / (amount * parents.Length * selected.Count()))) break; Object newObject = PrefabUtility.InstantiatePrefab(o, (!parent) ? SceneManager.GetActiveScene() : parent.scene); Undo.RegisterCreatedObjectUndo(newObject, "prefab creation"); createdObjects.Add(newObject); if (parent) { GameObject obj = newObject as GameObject; if (obj != null) { Undo.SetTransformParent(obj.transform, parent.transform, "re-parenting"); obj.transform.Reset(); } } } } k++; } j++; } EditorUtility.ClearProgressBar(); Selection.objects = createdObjects.ToArray(); Undo.CollapseUndoOperations(undoId); } public static AssetNameAutoComplete PrefabAssets() { return new AssetNameAutoComplete() { IncludeDirectories = false, CustomType = "GameObject" }; } [Command("New Scriptable Object", "Creates a new ScriptableObject of the type specified in the first folder selected", AlwaysShow = true, QuickName = "NSO", MenuItemLink = "NewScriptableObject", Order = -5, MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "Assets")] public static void CreateNewScriptableObject( [CommandParameter(Help = "The type of the ScriptableObject to create", ForceAutoCompleteUsage = true, PreventDefaultValueUsage = true, AutoCompleteMethodName = "ScriptableObjectAutoComplete")] Type type) { ScriptableObject obj = ScriptableObject.CreateInstance(type); NameAndCreateNewAsset(obj, "New" + type.Name, type.Name); } [Command("Attach New Material", "Creates and attaches a new material in the selected folder to the selected objects", ValidationMethodName = "ObjectsWithRenderer", QuickName = "AM", Category = "Assets")] public static void CreateAndAttachMaterial( [CommandParameter(Help = "the name of the shader to use for the new material", AutoCompleteMethodName = "ShaderAutoComplete", ForceAutoCompleteUsage = true, DefaultValueNameOverride = "Standard Shader")] string shaderName = "") { int undoGroup = MonkeyEditorUtils.CreateUndoGroup("New Material"); Shader shader = (shaderName.IsNullOrEmpty()) ? Shader.Find("Standard") : AssetDatabase.LoadAssetAtPath(shaderName); Material mat = new Material(shader); Object obj = (Object)mat; IEnumerable gosWithRenderer = Selection.gameObjects.Where(_ => _.GetComponentInChildren() != null); if (!NameAndCreateNewAsset(obj, "New Material", "Material")) return; foreach (GameObject o in gosWithRenderer) { Renderer rend = o.GetComponentInChildren(); Undo.RecordObject(rend, "Setting Material"); rend.sharedMaterial = mat; } Undo.CollapseUndoOperations(undoGroup); } [Command("Replace Material", "Replaces the first material on all the meshrenderers of the objects selected", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, QuickName = "RM", Category = "Assets")] public static void ReplaceMaterial( [CommandParameter("The material to replace the old one with", OverrideTypeName = "Material", ForceAutoCompleteUsage = true, PreventDefaultValueUsage = true, AutoCompleteMethodName = "MaterialAutoComplete")] string materialReplacement) { Material mat = AssetDatabase.LoadAssetAtPath(materialReplacement); int undoID = MonkeyEditorUtils.CreateUndoGroup("Replace Material"); foreach (MeshRenderer renderer in Selection.gameObjects.Convert(_ => _.GetComponentInChildren())) { Undo.RecordObject(renderer, "change of material"); if (renderer) renderer.sharedMaterial = mat; } Undo.CollapseUndoOperations(undoID); } public static GenericCommandParameterAutoComplete MaterialAutoComplete() { return new AssetNameAutoComplete() { CustomType = "Material" }; } public static bool NameAndCreateNewAsset(Object obj, string name, string type) { obj.name = name; string savePath; if (!Selection.objects.Any(_ => _.IsDirectory())) { savePath = MonkeyEditorUtils.GetProjectWindowFocusedFolder(); if (savePath.IsNullOrEmpty()) { Debug.Log("Monkey Create New " + type + ":" + "No Folder Selected, manual folder selection mode"); savePath = EditorUtility.SaveFilePanelInProject(type + " Creation", "New " + type, "", "Select the object's folder", "Assets"); } } else savePath = AssetDatabase.GetAssetPath(Selection.objects.First(_ => _.IsDirectory())); if (savePath.Length <= 0) return false; savePath += "/" + obj.name + ".asset"; EditorUtility.DisplayProgressBar("Object creation", "In Progress!", 1); savePath = AssetDatabase.GenerateUniqueAssetPath(savePath); AssetDatabase.CreateAsset(obj, savePath); EditorUtility.ClearProgressBar(); Selection.objects = new Object[] { obj }; EditorGUIUtility.PingObject(obj); return true; } [CommandValidation("Select some GameObjects with renderers")] private static bool ObjectsWithRenderer() { return Selection.gameObjects.Any(_ => _.GetComponentInChildren()); } private static AssetNameAutoComplete ShaderAutoComplete() { return new AssetNameAutoComplete() { IncludeDirectories = false, CustomType = "Shader" }; } [Command("New Scene", "Creates a new scene in the first selected folder, or the focused one in the project window", MenuItemLink = "NewScene", MenuItemLinkTypeOwner = "MonkeyMenuItems", QuickName = "NSC", Category = "Scene")] public static void CreateNewScene() { ProjectWindowUtil.CreateScene(); } [Command("New Folder", "Creates a new folder in the first selected folder, or the focused one in the project window", QuickName = "NF", Category = "Assets")] public static void CreateNewFolder() { ProjectWindowUtil.CreateFolder(); } [Command("Add Components", "Creates new components on the selected GameObjects, with the types specified", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, AlwaysShow = true, QuickName = "AC", MenuItemLink = "AddComponents", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "GameObject/Component")] public static void AddComponents( [CommandParameter(Help = "The types of components to add", ForceAutoCompleteUsage = true, PreventDefaultValueUsage = true, AutoCompleteMethodName = "ComponentAutoComplete")] Type[] componentTypes) { if (componentTypes == null || componentTypes.Length == 0) { Debug.LogWarning("Monkey Warning: Error parsing the component types"); return; } int undoID = MonkeyEditorUtils.CreateUndoGroup("Adding Components"); foreach (var gameObject in Selection.gameObjects) { foreach (var type in componentTypes) { if (type == null) continue; Component comp = gameObject.AddComponent(type); if (comp) Undo.RegisterCreatedObjectUndo(comp, "component attached"); } } Undo.CollapseUndoOperations(undoID); } [Command("Replace Components", "Replaces the first component of the type specified " + "by another one of another type on all objects", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, AlwaysShow = true, QuickName = "RC", MenuItemLink = "ReplaceComponents", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "GameObject/Component")] public static void ReplaceComponents( [CommandParameter(Help = "The type of component to replace", ForceAutoCompleteUsage = true, PreventDefaultValueUsage = true, AutoCompleteMethodName = "TypeAuto")] Type componentType, [CommandParameter(Help = "The types of components to add", ForceAutoCompleteUsage = true, PreventDefaultValueUsage = true, AutoCompleteMethodName = "ComponentAutoComplete")] Type newType) { if (componentType == null || newType == null) { Debug.LogWarning("Monkey Warning: Error parsing the component types"); return; } int undoID = MonkeyEditorUtils.CreateUndoGroup("Replacing Components"); foreach (var gameObject in Selection.gameObjects) { Component oldcomp = gameObject.GetComponent(componentType); if (oldcomp) Undo.DestroyObjectImmediate(oldcomp); Component comp = gameObject.AddComponent(newType); if (comp) Undo.RegisterCreatedObjectUndo(comp, "component attached"); } Undo.CollapseUndoOperations(undoID); } [Command("Remove Component", "Removes the first components of the type specified " + "on all objects", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, QuickName = "REC", Category = "GameObject/Component")] public static void RemoveComponents( [CommandParameter(Help = "The type of component to remove", ForceAutoCompleteUsage = true, PreventDefaultValueUsage = true, AutoCompleteMethodName = "TypeAuto")] Type componentType ) { if (componentType == null) { Debug.LogWarning("Monkey Warning: Error parsing the component types"); return; } int undoID = MonkeyEditorUtils.CreateUndoGroup("Removing Components"); foreach (var gameObject in Selection.gameObjects) { Component[] oldcomps = gameObject.GetComponents(componentType); foreach (var oldcomp in oldcomps) { if (oldcomp) Undo.DestroyObjectImmediate(oldcomp); } } Undo.CollapseUndoOperations(undoID); } public static TypeAutoComplete ScriptableObjectAutoComplete() { return new TypeAutoComplete(false, false, false); } public static TypeAutoComplete ObjectAutoComplete() { return new TypeAutoComplete(true, false, false, true, false); } public static TypeAutoComplete ComponentAutoComplete() { return new TypeAutoComplete(false, true, true, false); } [Command("Duplicate", "Duplicates the selected objects, by default once", DefaultValidation = DefaultValidation.AT_LEAST_ONE_OBJECT, MenuItemLink = "Duplicate", MenuItemLinkTypeOwner = "MonkeyMenuItems", QuickName = "D", Category = "GameObject")] public static void Duplicate( [CommandParameter(Help = "The amount of duplicates that will be created")] int amount = 1) { List createdObjects = new List(); int undoID = MonkeyEditorUtils.CreateUndoGroup("Duplicate"); foreach (GameObject toDup in Selection.gameObjects) { for (int i = 0; i < amount; i++) { if (EditorUtility.DisplayCancelableProgressBar("Duplicating Objects", "Duplicating " + toDup.name, (float)i / amount)) { Undo.CollapseUndoOperations(undoID); Selection.objects = createdObjects.ToArray(); EditorUtility.ClearProgressBar(); return; } Object newObj; GameObject go = null; #if UNITY_2019_1_OR_NEWER if (PrefabUtility.IsPartOfPrefabInstance(toDup)) { GameObject prefabParent = (GameObject) PrefabUtilities.GetPrefabParentPlatformIndependant(toDup); newObj = PrefabUtility.InstantiatePrefab(prefabParent, toDup.scene); go = newObj as GameObject; } else if ( /*!PrefabUtility.IsPartOfPrefabAsset(toDup) || */!toDup.scene.IsValid()) { continue; } else { newObj = Object.Instantiate(toDup); go = newObj as GameObject; } if (toDup.scene.IsValid()) #else if (PrefabUtility.GetPrefabType(toDup) == PrefabType.PrefabInstance) { GameObject prefabParent = (GameObject) PrefabUtilities.GetPrefabParentPlatformIndependant(toDup); newObj = PrefabUtility. InstantiatePrefab(prefabParent, toDup.scene); go = newObj as GameObject; } else if (!toDup.scene.IsValid()) { continue; } else { #if UNITY_2017_1_OR_NEWER newObj = Object.Instantiate(toDup, toDup.transform.parent); go = newObj as GameObject; #else newObj = Object.Instantiate(toDup); go = newObj as GameObject; #endif } #endif if (go) { go.transform.SetSiblingIndex(toDup.transform.GetSiblingIndex() + 1 + i); go.transform.SetParent(toDup.transform.parent); go.transform.CopyLocalPositionRotation(toDup.transform); go.transform.localScale = toDup.transform.localScale; } createdObjects.Add(newObj); Undo.RegisterCreatedObjectUndo(newObj, "new Duplicate"); EditorApplication.RepaintHierarchyWindow(); } } foreach (Object o in Selection.objects) { GameObject go = o as GameObject; #if UNITY_2019_1_OR_NEWER if (go && !PrefabUtility.IsPartOfPrefabAsset(go)) continue; #else if (go && Selection.transforms.Contains(go.transform)) continue; #endif for (int i = 0; i < amount; i++) { if (EditorUtility.DisplayCancelableProgressBar("Duplicating Objects", "Duplicating " + o.name, (float)i / amount)) { Undo.CollapseUndoOperations(undoID); Selection.objects = createdObjects.ToArray(); EditorUtility.ClearProgressBar(); return; } string path = AssetDatabase.GetAssetPath(o); string newPath = AssetDatabase.GenerateUniqueAssetPath(path); AssetDatabase.CopyAsset(path, newPath); Object newlyCreated = AssetDatabase.LoadAssetAtPath(newPath, typeof(Object)); //Undo.RegisterCreatedObjectUndo(newlyCreated, "new Duplicate"); createdObjects.Add(newlyCreated); } } Undo.CollapseUndoOperations(undoID); Selection.objects = createdObjects.Append(Selection.gameObjects).ToArray(); EditorUtility.ClearProgressBar(); EditorApplication.RepaintHierarchyWindow(); EditorApplication.DirtyHierarchyWindowSorting(); EditorApplication.RepaintProjectWindow(); } private static GameObject FirstSelected() { return MonkeyEditorUtils.OrderedSelectedGameObjects.FirstOrDefault(); } [Command("Duplicate And Mirror", "Duplicates an objects following a point symmetry" , QuickName = "DMP", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, Category = "GameObject")] public static void DuplicateMirrorPoint() { MonkeyEditorUtils.AddSceneCommand(new MirrorCreationCommand(Selection.activeGameObject)); } public class MirrorCreationCommand : ConfirmedCommand { public GameObject MirrorCenter; public GameObject ObjectToMirror; public bool UseRotation; public MirrorCreationCommand(GameObject toMirror) { SceneCommandName = "Mirror Creation"; ObjectToMirror = toMirror; } public override void DisplayParameters() { base.DisplayParameters(); DisplayObjectOption("Mirror Center", ref MirrorCenter); DisplayBoolOption("Use Rotation", ref UseRotation); DisplayObjectOption("Object To Mirror", ref ObjectToMirror); DisplayButton("Create Mirror", CreateNewMirror); } public void CreateNewMirror() { var go = ObjectToMirror.InstantiateObjectAsInstance(); go.transform.SetParent(ObjectToMirror.transform.parent); go.transform.SetSiblingIndex(ObjectToMirror.transform.GetSiblingIndex() + 1); Vector3 direction = MirrorCenter.transform.position - ObjectToMirror.transform.position; go.transform.position = MirrorCenter.transform.position + direction; if (UseRotation) { go.transform.rotation = Quaternion.Inverse(go.transform.rotation); } } } [Command("New Instances Under Mouse", QuickName = "NIM", Order = -6, Help = "Instantiates prefabs interactively at the mouse position, " + "randomly from a list of selected prefabs", AlwaysShow = true, MenuItemLink = "InstantiateMousePrefab", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "GameObject")] public static void InstantiatePrefabUnderMouse( [CommandParameter(Help = "The list of prefabs you want to instantiate," + " they will be randomly chosen", AutoCompleteMethodName = "PrefabAssets", ForceAutoCompleteUsage = true, DefaultValueMethod = "DefaultAssets", DefaultValueNameOverride = "The Selected Prefabs", OverrideTypeName = "Prefab Names")] string[] prefabs) { List prefabsObjects = new List(); foreach (string prefab in prefabs) { prefabsObjects.Add(AssetDatabase.LoadAssetAtPath(prefab)); } MonkeyEditorUtils.AddSceneCommand( new RaycastCreationSceneCommand(prefabsObjects, 0, DirectionAxis.UP, Vector2.zero, true)); } [Command("New Instances Under Mouse 2D", QuickName = "NIM2", Order = -6, Help = "Instantiates prefabs interactively at the mouse position in 2D, " + "randomly from a list of selected prefabs", AlwaysShow = true, Category = "2D/Creation")] public static void InstantiatePrefabUnderMouse2D( [CommandParameter(Help = "The list of prefabs you want to instantiate," + " they will be randomly chosen", AutoCompleteMethodName = "PrefabAssets", ForceAutoCompleteUsage = true, DefaultValueMethod = "DefaultAssets", DefaultValueNameOverride = "The Selected Prefabs", OverrideTypeName = "Prefab Names")] string[] prefabs) { List prefabsObjects = new List(); foreach (string prefab in prefabs) { prefabsObjects.Add(AssetDatabase.LoadAssetAtPath(prefab)); } MonkeyEditorUtils.AddSceneCommand(new Raycast2DCreationSceneCommand(prefabsObjects, Vector2.one, true)); } [Command("Duplicate Under Mouse", QuickName = "DM", AlwaysShow = true, Order = -6, Help = "Duplicates the selected objects randomly under the raycasted position of the mouse " + "(new instance for prefabs)", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, MenuItemLink = "DuplicateMouseRay", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "GameObject")] public static void DuplicateObjectUnderMouse() { MonkeyEditorUtils.AddSceneCommand( new RaycastCreationSceneCommand(Selection.gameObjects.ToList(), 0, DirectionAxis.UP, Vector2.zero, true)); } [Command("Duplicate Under Mouse 2D", QuickName = "DM2", AlwaysShow = true, Order = -6, Help = "Duplicates the selected objects randomly under the 2D position of the mouse " + "(new instance for prefabs)", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, Category = "2D/Creation")] public static void DuplicateObjectUnderMouse2D() { MonkeyEditorUtils.AddSceneCommand( new Raycast2DCreationSceneCommand(Selection.gameObjects.ToList(), Vector2.zero, true)); } [Command("New GameObjects Under Mouse", QuickName = "NG", Help = "Creates new objects under the raycasted position of the mouse", Category = "GameObject")] public static void CreateObjectUnderMouse() { MonkeyEditorUtils.AddSceneCommand( new RaycastCreationSceneCommand(null, 0, DirectionAxis.UP, Vector2.zero, false)); } [Command("New GameObjects Under Mouse 2D", QuickName = "NG2", Help = "Creates new objects under the 2D position of the mouse", Category = "2D/Creation")] public static void CreateObjectUnderMouse2D() { MonkeyEditorUtils.AddSceneCommand( new Raycast2DCreationSceneCommand(null, Vector2.one, false)); } public class RaycastCreationSceneCommand : ConfirmedCommand { public GameObject ParentObject; public float Offset = 0; private Vector3 lastProjectionPosition; private Vector3 lastProjectionNormal; private readonly List prefabs; private Vector2 scaleRandomRange; private List createdObjects = new List(); private bool collision; private DirectionAxis axisToNormal; private DirectionAxis forwardAxis; private bool orientAlongSurface; private Vector3? lastPositionCreated = null; private GameObject debugDrawObject; private GameObject lastCreated; public float angleOffset; public Vector2 RandomRotationRange; public float orientationFactor = 1; private bool IgnoreInvisibleColliders; public RaycastCreationSceneCommand(List prefabs, float offset, DirectionAxis axisToNormal, Vector2 scaleRandomRange, bool followCreationCurve) { ConfirmationMode = ActionConfirmationMode.ESCAPE; SceneCommandName = "RayCast Mouse Creation"; IgnoreInvisibleColliders = true; forwardAxis = DirectionAxis.FORWARD; scaleRandomRange = Vector2.one; this.prefabs = prefabs; Offset = offset; this.axisToNormal = axisToNormal; this.scaleRandomRange = scaleRandomRange; RandomRotationRange = new Vector2(-180, 180); if (UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage() != null) { //then by default the parent should be the current prefab ParentObject = UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage().prefabContentsRoot; } } public override string ConfirmationMessage() { return "Press SPACE or Click to instantiate, ESCAPE to stop"; } public override void DisplayParameters() { base.DisplayParameters(); DisplayBoolOption("Orient Along Surface", ref orientAlongSurface); if (orientAlongSurface) DisplayFloatOption("Orientation Factor", ref orientationFactor); DisplayVector2Option("Random Rotation Range", ref RandomRotationRange); DisplayVector2Option("Scale Range", ref scaleRandomRange); DisplayBoolOption("Ignore Invisible Colliders", ref IgnoreInvisibleColliders); DisplayObjectOption("Parent Object", ref ParentObject); if (prefabs != null) DisplayObjectListOption("Objects To Instantiate", prefabs); DisplayFloatOption("Surface Offset", ref Offset); DisplayFloatOption("Angle Offset", ref angleOffset); Enum axis = axisToNormal; DisplayEnumOption("Up Axis", ref axis); axisToNormal = (DirectionAxis)axis; axis = forwardAxis; DisplayEnumOption("Forward Axis", ref axis); forwardAxis = (DirectionAxis)axis; } public override void OnSceneGUI() { base.OnSceneGUI(); if (stopping) return; if (!lastCreated || Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Space || Event.current.type == EventType.MouseDown && Event.current.button == 0) { Event.current.Use(); if (lastCreated && RandomRotationRange != Vector2.zero) lastCreated.transform.rotation = Quaternion.AngleAxis(Random.Range(RandomRotationRange.x, RandomRotationRange.y), lastProjectionNormal) * lastCreated.transform.rotation; UpdateCreatedObject(); Selection.objects = new Object[0]; if (lastCreated) { lastCreated.SetActive(false); Undo.RegisterCreatedObjectUndo(lastCreated, "Mouse Raycast Object Creation"); } } if (lastCreated) lastCreated.transform.AlignTransformToCollision(lastProjectionPosition, lastProjectionNormal, true, axisToNormal, forwardAxis); if (orientAlongSurface && lastCreated) { if (lastPositionCreated != null) { Quaternion rotation = Quaternion.LookRotation( (Vector3)(lastPositionCreated - lastCreated.transform.position), lastCreated.transform.AxisToVector(axisToNormal, true)); var initialRotation = lastCreated.transform.rotation; lastCreated.transform.rotation = Quaternion.Lerp(initialRotation, initialRotation * rotation, orientationFactor); } lastPositionCreated = lastCreated.transform.position; } if (lastCreated) lastCreated.transform.rotation = Quaternion.AngleAxis(angleOffset, lastProjectionNormal) * lastCreated.transform.rotation; if (collision) { #if UNITY_2017_1_OR_NEWER Handles.zTest = CompareFunction.Always; #endif Handles.Label(lastProjectionPosition + lastProjectionNormal, "MonKey Creation", MonkeyStyle.Instance.CommandNameStyle); #if UNITY_2017_1_OR_NEWER Handles.zTest = CompareFunction.LessEqual; #endif Handles.color = MonkeyStyle.Instance.WindowColor.Alphaed(.1f); Handles.DrawSolidDisc(lastProjectionPosition + lastProjectionNormal * 0.05f, lastProjectionNormal, .5f); Handles.color = MonkeyStyle.Instance.WindowColor.Alphaed(.9f); Handles.DrawSolidDisc(lastProjectionPosition + lastProjectionNormal * 0.05f, lastProjectionNormal, .1f); Handles.DrawLine(lastProjectionPosition, lastProjectionPosition + lastProjectionNormal * 1); Handles.color = MonkeyStyle.Instance.WindowColor.Alphaed(.5f).Inverted(); #if UNITY_2017_1_OR_NEWER Handles.zTest = CompareFunction.Greater; #endif Handles.DrawWireDisc(lastProjectionPosition + lastProjectionNormal * 0.05f, lastProjectionNormal, .5f); Handles.DrawDottedLine(lastProjectionPosition, lastProjectionPosition + lastProjectionNormal * 1, 1); Handles.color = Color.white; } } private GameObject UpdateCreatedObject() { if (lastCreated) { lastCreated.SetActive(true); if (debugDrawObject) Object.DestroyImmediate(debugDrawObject); createdObjects.Add(lastCreated); if (ParentObject) { lastCreated.transform.SetParent(ParentObject.transform, true); } if (createdObjects.Count > 0) createdObjects = createdObjects.Where(_ => _ != null).ToList(); } GameObject go; if (prefabs == null || prefabs.Count == 0) go = new GameObject("Mouse Raycast Object " + createdObjects.Count); else { GameObject pref = prefabs.GetRandom(); if (!pref.IsPrefab()) { go = pref.InstantiateObjectAsInstance(); if (pref.scene.IsValid()) { go.transform.SetParent(pref.transform.parent); go.transform.SetSiblingIndex(pref.transform.GetSiblingIndex() + 1); } } else { go = (GameObject) PrefabUtility.InstantiatePrefab(pref, SceneManager.GetActiveScene()); } } if (!go) return null; debugDrawObject = new GameObject("MonKey Debug Draw"); MonKeyDebugMeshDrawer drawer = debugDrawObject.AddComponent(); drawer.reference = go; drawer.MeshColor = Color.white.Alphaed(.6f); drawer.OutlineColor = MonkeyStyle.Instance.WarningColor.Alphaed(.9f); lastCreated = go; lastCreated.transform.localScale = go.transform.localScale * Random.Range(scaleRandomRange.x, scaleRandomRange.y); return go; } public override void Update() { base.Update(); lastProjectionPosition = MonkeyEditorUtils.GetMouseRayCastedPosition(null, Offset, out collision, out lastProjectionNormal, IgnoreInvisibleColliders); } private bool stopping = false; public override void Stop() { if (lastCreated) { Object.DestroyImmediate(lastCreated); } if (debugDrawObject) { Object.DestroyImmediate(debugDrawObject); } base.Stop(); Selection.objects = createdObjects.ToArray(); stopping = true; } } public class Raycast2DCreationSceneCommand : ConfirmedCommand { private Vector2 lastProjectionPosition; private Vector2 lastProjectionNormal; private readonly List prefabs; private Vector2 scaleRandomRange; private List createdObjects = new List(); private bool collision; private DirectionAxis axisToNormal; private bool followCreationCurve; private Vector2? lastPositionCreated = null; public Raycast2DCreationSceneCommand(List prefabs, Vector2 scaleRandomRange, bool followCreationCurve) { ConfirmationMode = ActionConfirmationMode.ESCAPE; SceneCommandName = "RayCast Mouse Creation"; this.prefabs = prefabs; this.scaleRandomRange = scaleRandomRange; } public override string ConfirmationMessage() { return "Press SPACE to Instantiate a new object, ESCAPE to stop"; } public override void OnSceneGUI() { base.OnSceneGUI(); if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Space) { Event.current.Use(); GameObject go; if (prefabs == null || prefabs.Count == 0) go = new GameObject("Mouse Raycast Object " + createdObjects.Count); else { GameObject pref = prefabs.GetRandom(); if (!pref.IsPrefab()) { go = pref.InstantiateObjectAsInstance(); if (pref.scene.IsValid()) { go.transform.SetParent(pref.transform.parent); go.transform.SetSiblingIndex(pref.transform.GetSiblingIndex() + 1); } } else { go = (GameObject) PrefabUtility.InstantiatePrefab(pref, SceneManager.GetActiveScene()); } } createdObjects.Add(go); go.transform.AlignTransformToCollision(lastProjectionPosition, lastProjectionNormal, true, axisToNormal); if (followCreationCurve) { if (lastPositionCreated != null) { Quaternion rotation = Quaternion.LookRotation( (Vector3)(lastPositionCreated - go.transform.position), go.transform.AxisToVector(axisToNormal, true)); go.transform.rotation = go.transform.rotation * rotation; } lastPositionCreated = go.transform.position; } if (scaleRandomRange != Vector2.zero) go.transform.localScale = Vector3.one * Random.Range(scaleRandomRange.x, scaleRandomRange.y); Undo.RegisterCreatedObjectUndo(go, "Mouse Raycast Object Creation"); } createdObjects = createdObjects.Where(_ => _ != null).ToList(); if (collision) { #if UNITY_2017_1_OR_NEWER Handles.zTest = CompareFunction.Always; #endif Handles.Label(lastProjectionPosition + lastProjectionNormal, "MonKey Creation", MonkeyStyle.Instance.CommandNameStyle); #if UNITY_2017_1_OR_NEWER Handles.zTest = CompareFunction.LessEqual; #endif Handles.color = MonkeyStyle.Instance.WindowColor.Alphaed(.1f); Handles.DrawSolidDisc(lastProjectionPosition + lastProjectionNormal * 0.05f, lastProjectionNormal, .5f); Handles.color = MonkeyStyle.Instance.WindowColor.Alphaed(.9f); Handles.DrawSolidDisc(lastProjectionPosition + lastProjectionNormal * 0.05f, lastProjectionNormal, .1f); Handles.DrawLine(lastProjectionPosition, lastProjectionPosition + lastProjectionNormal * 1); Handles.color = MonkeyStyle.Instance.WindowColor.Alphaed(.5f).Inverted(); #if UNITY_2017_1_OR_NEWER Handles.zTest = CompareFunction.Greater; #endif Handles.DrawWireDisc(lastProjectionPosition + lastProjectionNormal * 0.05f, lastProjectionNormal, .5f); Handles.DrawDottedLine(lastProjectionPosition, lastProjectionPosition + lastProjectionNormal * 1, 1); Handles.color = Color.white; } } public override void Update() { base.Update(); if (!MonkeyEditorUtils.CurrentSceneView) MonkeyEditorUtils.CurrentSceneView = SceneView.currentDrawingSceneView; lastProjectionPosition = MonkeyEditorUtils.CurrentSceneView.camera.ScreenToWorldPoint(MonkeyEditorUtils .SceneViewMousePosition); } public override void Stop() { base.Stop(); Selection.objects = createdObjects.ToArray(); } } [Command("Replace Selected GameObjects", "Replaces the selection with objects specified", QuickName = "RS", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, Category = "GameObject")] public static void ReplaceSelection( [CommandParameter(Help = "The objects that will replace the selected ones." + " If less objects than selected ones, will loop", ForceAutoCompleteUsage = true, PreventDefaultValueUsage = true, AutoCompleteMethodName = "AssetObjectsAuto")] string[] toReplaceWith, [CommandParameter(Help = "should the objects to replace be chosen randomly")] bool replaceRandomly = true, [CommandParameter(Help = "Should the scale of the replaced objects be kept")] bool keepScale = false, [CommandParameter(Help = "Should the names of the replaced objects be kept")] bool keepNames = false) { int undoID = MonkeyEditorUtils.CreateUndoGroup("Replace Game Objects"); List gos = MonkeyEditorUtils.OrderedSelectedGameObjects.Where(_ => _.scene.IsValid()).ToList(); List newGos = new List(gos.Count); if (toReplaceWith == null) return; int i = 0; foreach (var go in gos) { if (i >= toReplaceWith.Length) i = 0; GameObject newGo; GameObject prefab = AssetDatabase.LoadAssetAtPath( toReplaceWith[replaceRandomly ? Random.Range(0, toReplaceWith.Length) : i]); #if UNITY_2019_1_OR_NEWER if (PrefabUtility.GetPrefabAssetType(prefab) != PrefabAssetType.NotAPrefab) { newGo = (GameObject)PrefabUtility.InstantiatePrefab(prefab, go.scene); } else { newGo = Object.Instantiate(prefab); } #else if (PrefabUtility.GetPrefabType(prefab) == PrefabType.Prefab) { newGo = (GameObject)PrefabUtility.InstantiatePrefab(prefab, go.scene); } else { newGo = Object.Instantiate(prefab); } #endif newGo.name = keepNames ? go.name : prefab.name; Undo.RegisterCreatedObjectUndo(newGo, "New Object Created"); newGo.transform.SetParent(go.transform.parent); if (keepScale) newGo.transform.CopyLocal(go.transform); else newGo.transform.CopyLocalPositionRotation(go.transform); newGo.SetActive(go.activeSelf); if (go) Undo.DestroyObjectImmediate(go); i++; } Selection.objects = newGos.Convert(_ => _ as Object).ToArray(); Undo.CollapseUndoOperations(undoID); } public static AssetNameAutoComplete AssetObjectsAuto() { return new AssetNameAutoComplete() { IncludeDirectories = false, CustomType = "GameObject" }; } private static Type componentCopiedType; [Command("Copy Component", "Copies the first component of the first selected object " + "of the type specified to use later", QuickName = "CC", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, MenuItemLink = "CopyComponent", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "GameObject/Component")] public static void CopyComponent( [CommandParameter(Help = "The type of component to copy", AutoCompleteMethodName = "TypeAuto", ForceAutoCompleteUsage = true)] Type type) { componentCopiedType = type; if (!MonkeyEditorUtils.OrderedSelectedGameObjects.Any()) { Debug.LogWarning("Monkey Copy Component:".Bold() + " GameObjects were deselected before the method could complete"); return; } GameObject first = MonkeyEditorUtils.OrderedSelectedGameObjects.ElementAt(0); Component comp = first.GetComponent(type); ComponentUtility.CopyComponent(comp); } /// /// creates an auto complete that returns all the component types attached to a gameobject /// /// public static TypeAutoComplete TypeAuto() { TypeAutoComplete auto = new TypeAutoComplete(false, false, false, false); foreach (GameObject gameObject in Selection.gameObjects) { foreach (var component in gameObject.GetComponents()) { if (!component) continue; Type type = component.GetType(); while (type != typeof(Component)) { if (!auto.ContainsKey(type.Name)) auto.AddNewType(type.Name, type); type = type.BaseType; if (type == null || type == typeof(Object)) { //never too sure break; } } } } return auto; } private static CommandParameterObjectAutoComplete RigidBodyAutoComplete() { CommandParameterObjectAutoComplete autoComplete = new CommandParameterObjectAutoComplete(); foreach (var gameObject in Selection.gameObjects) { Rigidbody rb = gameObject.GetComponent(); if (rb) { autoComplete.AddValue(gameObject.name, rb); } } return autoComplete; } [Command("Paste Component As New", QuickName = "PN", Help = "Paste the previously copied component as a new one on the selected gameObjects ", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, MenuItemLink = "PasteComponentAsNew", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "GameObject/Component")] public static void PasteComponentAsNew() { foreach (GameObject o in Selection.gameObjects) { ComponentUtility.PasteComponentAsNew(o); } } [Command("Paste Component Value", "Paste the previously copied component values " + "in place of the first one of its type in the selected object", QuickName = "PV", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, MenuItemLink = "PasteComponentValues", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "GameObject/Component")] public static void ReplaceComponent() { if (componentCopiedType == null) return; foreach (GameObject o in Selection.gameObjects) { Component comp = o.GetComponent(componentCopiedType); if (comp) ComponentUtility.PasteComponentValues(comp); } } [Command("Paste Component Value All", "Paste the previously copied component values " + "in place of all of its type in the selected object", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, QuickName = "PVA", Category = "GameObject/Component")] public static void ReplaceComponents() { if (componentCopiedType == null) return; foreach (GameObject o in Selection.gameObjects) { Component[] comp = o.GetComponents(componentCopiedType); foreach (var component in comp) { ComponentUtility.PasteComponentValues(component); } } } [Command("Duplicate On Axis", QuickName = "DA", Help = "Duplicates the selected object along the axis indicated, separated by a given distance", DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM, MenuItemLink = "DuplicateOnAxis", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "GameObject")] public static void DuplicateOnAxis() { MonkeyEditorUtils.AddSceneCommand(new DuplicateAxisSceneCommand(Selection.gameObjects, 3, 1)); } public class DuplicateAxisSceneCommand : TimedSceneCommand { public List ToDup = new List(); public Vector3 Offset; public bool LocalAxis; public DirectionAxis Axis; public int DuplicateAmount; public float DuplicateDistance; private readonly Dictionary> duplicated = new Dictionary>(); private bool applyDuplicate = false; public DuplicateAxisSceneCommand(GameObject[] toDuplicate, int duplicateAmount, float duplicateDistance) : base(0) { SceneCommandName = "Duplicate On Axis"; ToDup.AddRange(toDuplicate); DuplicateAmount = duplicateAmount; DuplicateDistance = duplicateDistance; foreach (var o in ToDup) { duplicated.Add(o, new List()); } Duplicate(); } private List toRemove = new List(); public override void DisplayParameters() { int preAmount = DuplicateAmount; DisplayIntOption("Duplicate Amount", ref DuplicateAmount); if (DuplicateAmount < 1) DuplicateAmount = 1; float prevDistance = DuplicateDistance; DisplayFloatOption("Duplicate Distance", ref DuplicateDistance); DisplayVectorOption("Offset", ref Offset); if (DuplicateDistance < 0) DuplicateDistance = 0; DisplayBoolOption("Use Local Axis", ref LocalAxis); DirectionAxis prevAxis = Axis; Enum ax = Axis; DisplayEnumOption("Axis", ref ax); Axis = (DirectionAxis)ax; bool changed = DisplayObjectListOption("Objects To Duplicate", ToDup) != -1; if (changed) ResolveNewObjects(); if (preAmount != DuplicateAmount || changed || !Mathf.Approximately(prevDistance, DuplicateDistance) || ToDup.Any(_ => _.transform.hasChanged) || prevAxis != Axis) Duplicate(); DisplayButton("Refresh Duplicate", Duplicate); DisplayButton("Apply Duplicate", ApplyDuplicate); } private void ResolveNewObjects() { toRemove.Clear(); foreach (var pair in duplicated) { if (ToDup.Contains(pair.Key)) continue; foreach (var o in pair.Value) { Object.DestroyImmediate(o); } toRemove.Add(pair.Key); } foreach (var gameObject in toRemove) { duplicated.Remove(gameObject); } foreach (var gameObject in ToDup) { if (!duplicated.ContainsKey(gameObject)) duplicated.Add(gameObject, new List()); } } private void ApplyDuplicate() { applyDuplicate = true; Stop(); } public void Duplicate() { if (stopped) return; if (ToDup.Count == 0) { DestroyAll(); return; } if (duplicated[ToDup[0]].Count > DuplicateAmount) { foreach (var pair in duplicated) { for (int i = 0; i < pair.Value.Count - DuplicateAmount; i++) { Object.DestroyImmediate(pair.Value[i]); } pair.Value.RemoveAll(_ => !_); } } else if (duplicated[ToDup[0]].Count < DuplicateAmount) { int count = duplicated[ToDup[0]].Count; foreach (var pair in duplicated) { for (int i = 0; i < DuplicateAmount - count; i++) { var dup = pair.Key.InstantiateObjectAsInstance(); dup.transform.localScale = dup.transform.localScale; dup.transform.SetParent(pair.Key.transform.parent, true); pair.Value.Add(dup); } } } foreach (var pair in duplicated) { Vector3 direction = pair.Key.transform.AxisToVector(Axis, LocalAxis); Vector3 currentOff = LocalAxis ? pair.Key.transform.TransformDirection(Offset) : Offset; float distance = DuplicateDistance; for (int i = 0; i < DuplicateAmount; i++) { var newGo = pair.Value[i]; newGo.transform.position = pair.Key.transform.position + distance * direction + currentOff * (i + 1); newGo.transform.rotation = pair.Key.transform.rotation; distance += DuplicateDistance; } } } private void DestroyAll() { foreach (var pair in duplicated) { foreach (var gameObject in pair.Value) { Object.DestroyImmediate(gameObject); } pair.Value.Clear(); } } private bool stopped = false; public override void Stop() { if (!applyDuplicate) { DestroyAll(); stopped = true; base.Stop(); return; } int undoId = MonkeyEditorUtils.CreateUndoGroup("MonKey Object Duplicate"); foreach (var pair in duplicated) { foreach (var gameObject in pair.Value) { Undo.RegisterCreatedObjectUndo(gameObject, gameObject.name); } Selection.objects = Selection.objects.Append(pair.Value.Convert(_ => _ as Object)).ToArray(); } Undo.CollapseUndoOperations(undoId); base.Stop(); } } [Command("New GameObject With Components", "Creates a new object with the selected component types", QuickName = "NGC", Category = "GameObject/Component")] public static void CreateObjectWithComponents( [CommandParameter("The types of components to add", AutoCompleteMethodName = "ComponentAutoComplete")] Type[] componentTypes) { int undoID = MonkeyEditorUtils.CreateUndoGroup("Object with Components"); GameObject go = new GameObject(); Undo.RegisterCreatedObjectUndo(go, "New Object created"); foreach (Type componentType in componentTypes) { Component comp = go.AddComponent(componentType); Undo.RegisterCreatedObjectUndo(comp, "New component created"); } Selection.objects = new Object[] { go }; Undo.CollapseUndoOperations(undoID); } [Command("New Primitive Under Mouse", "Creates a new primitive object under the mouse", QuickName = "NPM", Category = "GameObject")] public static void CreatePrimitiveMouseRaycast( [CommandParameter("The primitive type to create")] PrimitiveType primitiveType = PrimitiveType.Cube) { MonkeyEditorUtils.AddSceneCommand( new MoveUtilities.RaycastSceneCommand( new[] { GameObject.CreatePrimitive(primitiveType) }, .5f, DirectionAxis.UP)); } [Command("New Instances Between", QuickName = "NB", Help = "Creates new prefab instances between the two objects", MenuItemLink = "CreateInstancesBetween", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "GameObject")] public static void InstantiateBetweenTwo( [CommandParameter(Help = "The list of prefabs you want to instantiate", AutoCompleteMethodName = "PrefabAssets", ForceAutoCompleteUsage = true, DefaultValueMethod = "DefaultAssets", DefaultValueNameOverride = "The Selected Prefabs", OverrideTypeName = "Prefab Names")] string[] prefabNames) { GameObject[] selected = prefabNames.Convert(AssetDatabase.LoadAssetAtPath).ToArray(); MonkeyEditorUtils.AddSceneCommand(new NewInstancesBetweenSceneCommand(selected)); } public class NewInstancesBetweenSceneCommand : TimedSceneCommand { public GameObject StartObject; public GameObject EndObject; public List ObjectsToCreate; private List prevObjectsToCreate; public int Amount = 1; private List createdObjects = new List(); private bool commitCreated = false; private bool stopped = false; public NewInstancesBetweenSceneCommand(GameObject[] objs) : base(0) { SceneCommandName = "New Instance Between"; ObjectsToCreate = objs.ToList(); StartObject = MonKeySelectionUtils.OrderedGameObjects.FirstOrDefault(); EndObject = MonKeySelectionUtils.OrderedGameObjects.ElementAtOrDefault(1); prevObjectsToCreate = new List(ObjectsToCreate); } public override void DisplayParameters() { base.DisplayParameters(); var prevStart = StartObject; var prevEnd = EndObject; var prevAmount = Amount; DisplayObjectOption("Start Object", ref StartObject); DisplayObjectOption("End Object", ref EndObject); DisplayObjectListOption("Objects To Create", ObjectsToCreate); DisplayIntOption("Amount To Create", ref Amount); bool shouldRegenerate = false; if (ObjectsToCreate.Count != prevObjectsToCreate.Count) { shouldRegenerate = true; } else { for (int i = 0; i < ObjectsToCreate.Count; i++) { if (prevObjectsToCreate[i] != ObjectsToCreate[i]) { shouldRegenerate = true; break; } } } if (shouldRegenerate) { foreach (var gameObject in createdObjects) { Object.DestroyImmediate(gameObject); } createdObjects.Clear(); prevObjectsToCreate = ObjectsToCreate; } if (StartObject && EndObject && (prevAmount != Amount || shouldRegenerate || prevStart != StartObject || prevEnd != EndObject || StartObject.transform.hasChanged || EndObject.transform.hasChanged)) { Create(); } DisplayButton("Apply New Instances", Apply); } private void Apply() { commitCreated = true; Stop(); } public override void Stop() { if (!commitCreated) { foreach (var gameObject in createdObjects) { Object.DestroyImmediate(gameObject); } createdObjects.Clear(); stopped = true; base.Stop(); return; } int undoId = MonkeyEditorUtils.CreateUndoGroup("MonKey Object Creation"); foreach (var gameObject in createdObjects) { Undo.RegisterCreatedObjectUndo(gameObject, gameObject.name); } Undo.CollapseUndoOperations(undoId); base.Stop(); } public void Create() { if (stopped || !StartObject || !EndObject) return; if (createdObjects.Count > Amount) { for (int i = Amount; i < createdObjects.Count; i++) { if (!createdObjects[i]) continue; Object.DestroyImmediate(createdObjects[i]); } createdObjects.RemoveAll(_ => !_); } else if (createdObjects.Count < Amount) { int count = createdObjects.Count; int k = 0; for (int i = 0; i < Amount - count; i++) { var o = ObjectsToCreate[k]; // Debug.Log(o); k++; if (k >= ObjectsToCreate.Count) k = 0; /* if (EditorUtility.DisplayCancelableProgressBar("Instantiating Prefabs", "Instantiating " + o.name, (float)i / (Amount))) break;*/ var dup = o.InstantiateObjectAsInstance(); createdObjects.Add(dup); } // EditorUtility.ClearProgressBar(); } Vector3 direction = EndObject.transform.position - StartObject.transform.position; float distance = direction.magnitude / (Amount + 1); Vector3 currentPosition = StartObject.transform.position + distance * direction.normalized; for (int i = 0; i < Amount; i++) { GameObject go = createdObjects[i]; if (go) { go.transform.position = currentPosition; currentPosition += distance * direction.normalized; } } } } public static string[] DefaultAssets() { List selectedAssets = new List(); foreach (GameObject o in Selection.gameObjects) { if (o.scene.IsValid()) continue; selectedAssets.Add(AssetDatabase.GetAssetPath(o)); } return selectedAssets.ToArray(); } #if UNITY_2019_1_OR_NEWER [Command("New Material", "Creates a new Material with a default shader", QuickName = "NM")] public static void NewMaterial( [CommandParameter("The shader to use", AutoCompleteMethodName = "ShaderTypes", ForceAutoCompleteUsage = false, DefaultValueNameOverride = "Default Pipeline Shader", DefaultValueMethod = "DefaultShader")] ShaderInfo shaderInfo) { Shader source = Shader.Find(shaderInfo.name); Material mat = new Material(source); AssetDatabase.CreateAsset(mat, MonkeyEditorUtils.GetProjectWindowFocusedFolder() + "/New Material.mat"); Selection.objects = new Object[] { mat }; ProjectWindowUtil.ShowCreatedAsset(mat); } public static ShaderInfo DefaultShader() { switch (MonkeyEditorUtils.GetPipelineType()) { case MonkeyEditorUtils.PipelineType.HDRP: return ShaderUtil.GetAllShaderInfo().First(_ => _.name == "HDRP/Lit"); case MonkeyEditorUtils.PipelineType.URP: return ShaderUtil.GetAllShaderInfo().First(_ => _.name == "Universal Render Pipeline/Lit"); default: return ShaderUtil.GetAllShaderInfo().First(_ => _.name == "Standard"); } } public static StaticCommandParameterAutoComplete ShaderTypes() { StaticCommandParameterAutoComplete autoComp = new StaticCommandParameterAutoComplete(typeof(ShaderInfo)); var infos = ShaderUtil.GetAllShaderInfo(); var dic = new Dictionary(); foreach (var shaderInfo in infos) { if (shaderInfo.supported && !shaderInfo.hasErrors && !shaderInfo.name.Contains("Hidden") && !shaderInfo.name.Contains("Legacy")) dic.Add(shaderInfo.name, shaderInfo); } autoComp.ObjectsPerName = dic; return autoComp; } #endif } } #endif