#if UNITY_EDITOR using MonKey.Editor.Internal; using MonKey.Extensions; using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; namespace MonKey.Editor.Commands { public static class MoveUtilities { [Command("Move Under Mouse", Help = "Moves the selected objects to the position raycasted from the mouse", QuickName = "MM", AlwaysShow = true, DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM, MenuItemLink = "MoveMouseRaycast", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "Transform")] public static void MoveObjectUnderMouse() { MonkeyEditorUtils.AddSceneCommand(new RaycastSceneCommand(Selection.gameObjects, 0, DirectionAxis.UP)); } public class RaycastSceneCommand : ConfirmedCommand { public GameObject[] CachedObjectsToMove; public float Offset = 0; public float AngleOffset = 0; public DirectionAxis AlignToNormal; public DirectionAxis ForwardAlign; public bool IgnoreInvisibleColliders; private Transform[] selfAndChildren; struct PositionRotation { public readonly Vector3 Position; public readonly Quaternion Rotation; public PositionRotation(Vector3 position, Quaternion rotation) { this.Position = position; this.Rotation = rotation; } } private readonly List previousPosRot = new List(); public RaycastSceneCommand(GameObject[] cachedObjectsToMove, float offset, DirectionAxis alignToNormal) { // HideGUI = true; ConfirmationMode = ActionConfirmationMode.ENTER_AND_CLICK; SceneCommandName = "RayCast Mouse Position"; IgnoreInvisibleColliders = true; CachedObjectsToMove = cachedObjectsToMove; Offset = offset; AlignToNormal = alignToNormal; ForwardAlign = DirectionAxis.FORWARD; List allChildren = new List(); foreach (GameObject gameObject in cachedObjectsToMove) { previousPosRot.Add( new PositionRotation(gameObject.transform.position, gameObject.transform.rotation)); allChildren.AddRange(gameObject.GetComponentsInChildren()); } selfAndChildren = allChildren.ToArray(); } public override void DisplayParameters() { base.DisplayParameters(); DisplayFloatOption("Collision Offset", ref Offset); DisplayFloatOption("Angle Offset", ref AngleOffset); Enum en = AlignToNormal; DisplayEnumOption("Up Axis", ref en); AlignToNormal = (DirectionAxis) en; en = ForwardAlign; DisplayEnumOption("Forward Axis", ref en); ForwardAlign = (DirectionAxis) en; DisplayBoolOption("Ignore Invisible Colliders", ref IgnoreInvisibleColliders); } public override void Update() { base.Update(); bool collision = false; Vector3 normal; selfAndChildren = selfAndChildren.Where(_ => _).ToArray(); var position = MonkeyEditorUtils.GetMouseRayCastedPosition(selfAndChildren, Offset, out collision, out normal, IgnoreInvisibleColliders); if (collision) { // CachedObjectsToMove = CachedObjectsToMove.Where(_ => _).ToArray(); foreach (GameObject gameObject in CachedObjectsToMove) { if (!gameObject) continue; gameObject.transform.AlignTransformToCollision(position, normal, true, AlignToNormal, ForwardAlign); gameObject.transform.rotation = Quaternion.AngleAxis(AngleOffset, normal) * gameObject.transform.rotation; } } } public override void Stop() { base.Stop(); List newPosRot = new List(CachedObjectsToMove.Length); foreach (var gameObject in CachedObjectsToMove) { if (!gameObject) continue; newPosRot.Add( new PositionRotation(gameObject.transform.position, gameObject.transform.rotation)); } for (int i = 0; i < previousPosRot.Count; i++) { if (! CachedObjectsToMove[i]) continue; CachedObjectsToMove[i].transform.position = previousPosRot[i].Position; CachedObjectsToMove[i].transform.rotation = previousPosRot[i].Rotation; } int undoIndex = MonkeyEditorUtils.CreateUndoGroup("Mouse Raycast"); for (int i = 0; i < newPosRot.Count; i++) { if (! CachedObjectsToMove[i]) continue; Undo.RecordObject(CachedObjectsToMove[i].transform, "Applying new position"); CachedObjectsToMove[i].transform.position = newPosRot[i].Position; CachedObjectsToMove[i].transform.rotation = newPosRot[i].Rotation; } CachedObjectsToMove = CachedObjectsToMove.Where(_ => _).ToArray(); Selection.objects = CachedObjectsToMove.Convert(_ => _ as Object).ToArray(); Undo.CollapseUndoOperations(undoIndex); } } [Command("Copy Position", Help = "Copies the position of an object, by default the first object selected", QuickName = "CP", DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM, MenuItemLink = "CopyPosition", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "Transform")] public static void CopyPosition() { MonkeyEditorUtils.AddSceneCommand(new TransformCopyCommand(Selection.gameObjects) {CopyPosition = true,}); } public class TransformCopyCommand : TimedSceneCommand { public Transform TransformToCopy; public bool LocalValuesOnly; public bool CopyPosition; public bool CopyRotation; public bool CopyScale; public List Targets = new List(); public TransformCopyCommand(params GameObject[] objs) : base(0) { Targets = new List(objs); } public override void DisplayParameters() { base.DisplayParameters(); SceneCommandName = "Copy Transform"; DisplayObjectOption("Transform To Copy", ref TransformToCopy); DisplayBoolOption("Local Values Only", ref LocalValuesOnly); DisplayBoolOption("Copy Position", ref CopyPosition); DisplayBoolOption("Copy Rotation", ref CopyRotation); DisplayBoolOption("Copy Scale", ref CopyScale); DisplayObjectListOption("Targets", Targets); } public override void Update() { base.Update(); if (!TransformToCopy) return; foreach (GameObject o in Targets) { if (!o) continue; if (CopyPosition) { if (!LocalValuesOnly) o.transform.position = TransformToCopy.position; else o.transform.localPosition = TransformToCopy.localPosition; } if (CopyRotation) { if (!LocalValuesOnly) o.transform.rotation = TransformToCopy.rotation; else o.transform.localRotation = TransformToCopy.localRotation; } if (CopyScale) { if (!LocalValuesOnly) o.transform.SetLossyGlobalScale(TransformToCopy.lossyScale); else o.transform.localScale = TransformToCopy.localScale; } } } } [Command("Copy Rotation", Help = "Copies the rotation of an object, by default the first object selected", QuickName = "CR", DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM, MenuItemLink = "CopyRotation", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "Transform")] public static void CopyRotation() { MonkeyEditorUtils.AddSceneCommand(new TransformCopyCommand(Selection.gameObjects) {CopyRotation = true}); } [Command("Copy Transform", Help = "Copies the transform (scale ignored) of an object," + " by default the first object selected", QuickName = "CT", DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM, MenuItemLink = "CopyTransform", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "Transform")] public static void CopyTransform() { MonkeyEditorUtils.AddSceneCommand(new TransformCopyCommand(Selection.gameObjects) {CopyPosition = true, CopyRotation = true, CopyScale = true}); } private static GameObject ActiveObject() { return DefaultValuesUtilities.DefaultFirstGameObjectSelected(); } public static void RaycastDirection(bool local, Transform t, Vector3 direction, DirectionAxis axisToHitNormal) { RaycastHit hit; if (Physics.Raycast(t.position, local ? t.TransformDirection(direction) : direction, out hit)) { Undo.RecordObject(t, "movement"); t.AlignTransformToCollision(hit.point, hit.normal, true, axisToHitNormal); } } public static void RaycastDirection2D(bool local, Transform t, Vector3 direction, DirectionAxis axisToHitNormal, GameObject[] objectsToIgnore) { RaycastHit2D[] hits = Physics2D.RaycastAll(t.position, local ? t.TransformDirection(direction) : direction) .OrderByDescending(_ => (_.point - (Vector2) t.position).magnitude).ToArray(); foreach (var hit in hits) { if (objectsToIgnore.Contains(hit.transform.gameObject) || objectsToIgnore.Any(_ => _.GetComponentsInChildren().Contains(hit.transform))) continue; if (hit) { Undo.RecordObject(t, "movement"); t.AlignTransformToCollision(hit.point, hit.normal, true, axisToHitNormal); } else { Debug.Log("Nope"); } } } public static Axis[] DefaultLockedAxis() { return new[] {Axis.NONE}; } public static GameObject FirstSelected() { if (!MonkeyEditorUtils.OrderedSelectedGameObjects.Any()) return null; return MonkeyEditorUtils.OrderedSelectedGameObjects.ElementAt(0); } public static Vector3 ZeroVector() { return Vector3.zero; } [Command("Rotate Around", "Rotates the selected objects around a specified one (by default the first selected)," + " on one of its local axes", QuickName = "RA", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, Category = "Transform")] public static void RotateAround() { MonkeyEditorUtils.AddSceneCommand(new RotateAroundSceneCommand(Selection.gameObjects.ToList())); } public class RotateAroundSceneCommand : TimedSceneCommand { public float Angle = 90; public GameObject ReferenceObject; public Axis RotationAxis = Axis.Y; public List ObjectsToRotate; public RotateAroundSceneCommand(List objectsToRotate) : base(0) { ObjectsToRotate = objectsToRotate; SceneCommandName = "Rotate Around"; } public void Rotate() { int undoID = MonkeyEditorUtils.CreateUndoGroup("Rotate Around"); foreach (var gameObject in ObjectsToRotate) { if (gameObject == ReferenceObject) continue; Undo.RecordObject(gameObject.transform, "rotated"); gameObject.transform.RotateAround(ReferenceObject.transform.position, ReferenceObject.transform.AxisToVector(RotationAxis, true), Angle); } Undo.CollapseUndoOperations(undoID); } public override void DisplayParameters() { base.DisplayParameters(); DisplayObjectOption("Rotation Center", ref ReferenceObject); Enum en = RotationAxis; DisplayEnumOption("Rotation Axis", ref en); RotationAxis = (Axis) en; DisplayFloatOption("Angle", ref Angle); DisplayObjectListOption("Objects To Rotate", ObjectsToRotate); DisplayButton("Rotate", Rotate); } } [Command("Rotate", "Rotates the selected objects on their own axes", QuickName = "RO", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, Category = "Transform/Set")] public static void Rotate( [CommandParameter("The axis of rotation, as the local axis of the object")] Axis rotationAxis = Axis.Y, [CommandParameter("The angle of rotation, in degrees")] float angle = 90, [CommandParameter("Should the rotation be according to local axes")] bool local = true) { int undoID = MonkeyEditorUtils.CreateUndoGroup("Rotate"); foreach (GameObject o in Selection.gameObjects) { Undo.RecordObject(o.transform, "rotated"); o.transform.Rotate(o.transform.AxisToVector(rotationAxis, local), angle); } Undo.CollapseUndoOperations(undoID); } [Command("Reset Transforms", "Sets all the local values of the selected transform to default", QuickName = "RT", DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM, MenuItemLink = "ResetTransforms", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "Transform/Set")] public static void ResetTransforms() { int undoID = MonkeyEditorUtils.CreateUndoGroup("Reset Transforms"); foreach (Transform transform in Selection.transforms) { Undo.RecordObject(transform, "reset"); transform.Reset(); } Undo.CollapseUndoOperations(undoID); } [Command("Reset Rotation", "Sets all the local rotations of the selected transform to default", QuickName = "RR", DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM, MenuItemLink = "ResetRotations", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "Transform/Set")] public static void ResetRotations() { int undoID = MonkeyEditorUtils.CreateUndoGroup("Reset Transforms"); foreach (Transform transform in Selection.transforms) { Undo.RecordObject(transform, "reset"); transform.rotation = Quaternion.identity; } Undo.CollapseUndoOperations(undoID); } [Command("Set Parent", "Sets the parent of the selected objects", QuickName = "SP", DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM, MenuItemLink = "SetParent", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "Transform/Set")] public static void SetParent( [CommandParameter(Help = "The new parent of the selected objects", ForceAutoCompleteUsage = true, PreventDefaultValueUsage = true)] GameObject newParent) { int undoGroup = MonkeyEditorUtils.CreateUndoGroup("Change Parent"); foreach (var go in Selection.gameObjects) { Undo.SetTransformParent(go.transform, newParent.transform, "Reparent"); } Undo.CollapseUndoOperations(undoGroup); EditorGUIUtility.PingObject(newParent); } [Command("Set Parent Dynamic", "Sets the parent of the selected objects, as a scene command", QuickName = "SPD", Category = "Transform/Set")] public static void SetParentDynamic() { MonkeyEditorUtils.AddSceneCommand(new SetParentCommand(Selection.activeGameObject)); } public class SetParentCommand : SceneCommand { public GameObject Parent; public bool PreservePositionOnParenting = true; public SetParentCommand(GameObject parent) { Parent = parent; SceneCommandName = "Set Parent Dynamic"; } public override void Update() { //do nothing } public override void DisplayParameters() { base.DisplayParameters(); base.DisplayObjectOption("Parent Object", ref Parent); DisplayBoolOption("Preserve Position On Parenting", ref PreservePositionOnParenting); DisplayMessage("Reparents Selected Objects under the specified parent"); DisplayButton("Reparent", ApplyFunction); } public void ApplyFunction() { foreach (var gameObject in Selection.gameObjects) { gameObject.transform.SetParent(Parent ? Parent.transform : null, PreservePositionOnParenting); } } } [Command("Set Pivot On Transform", QuickName = "MP", Help = "set the position and rotation of the selected transforms as pivot, " + "which means that their children will not move or rotate", DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM, MenuItemLink = "MovePivot", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "Transform")] public static void MoveAsPivot() { MonkeyEditorUtils.AddSceneCommand(new PivotMoveSceneCommand(Selection.gameObjects)); } public class PivotMoveSceneCommand : TimedSceneCommand { public List PivotsToMove = new List(); public Transform ToCopyOn; public bool CopyRotation; private Vector3 lastPosition; private Quaternion lastRotation; public PivotMoveSceneCommand(params GameObject[] pivots) : base(0) { SceneCommandName = "Move Pivot"; PivotsToMove.AddRange(pivots); lastRotation = Quaternion.identity; } public override void DisplayParameters() { Transform previous = ToCopyOn; DisplayObjectOption("Transform To Copy", ref ToCopyOn); if (!ToCopyOn == previous) { Copy(); } DisplayBoolOption("Copy Rotation", ref CopyRotation); DisplayObjectListOption("Pivots To Move", PivotsToMove); } public override void Update() { if (!ToCopyOn) return; Vector3 distance = ToCopyOn.position - lastPosition; var angle = Quaternion.Angle(ToCopyOn.rotation, lastRotation); if (!ToCopyOn || !ToCopyOn.hasChanged || (distance.magnitude < 0.001f && (!CopyRotation || angle < 0.01f))) return; Copy(); } private void Copy() { lastPosition = ToCopyOn.position; lastRotation = ToCopyOn.rotation; int undoID = MonkeyEditorUtils.CreateUndoGroup("Move Pivot"); foreach (var go in Selection.gameObjects) { if (go == ToCopyOn) continue; Transform[] children = go.GetComponentsInChildren() .Where(_ => _ != go.transform).ToArray(); foreach (Transform child in children) { Vector3 position = child.position; Quaternion rotation = child.rotation; Undo.SetTransformParent(child, null, "movedObjectsOut"); child.position = position; child.rotation = rotation; } Undo.RecordObject(go.transform, "moving pivot"); go.transform.position = ToCopyOn.transform.position; if (CopyRotation) go.transform.rotation = ToCopyOn.transform.rotation; foreach (Transform child in children) { Vector3 position = child.position; Quaternion rotation = child.rotation; Undo.SetTransformParent(child, go.transform, "movedObjectsOut"); child.position = position; child.rotation = rotation; } } Undo.CollapseUndoOperations(undoID); } } [Command("Center Pivot", QuickName = "CPI", Help = "Moves the selected transforms as pivot " + "to the center of all their children transform", DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM, MenuItemLink = "CenterPivot", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "Transform")] public static void MovePivotToChildrenCenter() { int undoID = MonkeyEditorUtils.CreateUndoGroup("Move Pivot"); foreach (var go in Selection.gameObjects) { Transform[] children = go.GetComponentsInChildren() .Where(_ => _ != go.transform).ToArray(); Vector3[] childrenPos = children.Convert(_ => _.position).ToArray(); Vector3 weightCenter = new Vector3(); foreach (Transform child in children) { weightCenter += child.position; } weightCenter /= children.Length; Undo.RecordObject(go.transform, "moving pivot"); go.transform.position = weightCenter; for (int i = 0; i < children.Length; i++) { Undo.RecordObject(children[i], "moving"); children[i].position = childrenPos[i]; } } Undo.CollapseUndoOperations(undoID); } [Command("Move Assets To Folder", "Moves the selected asset to the specified folder", QuickName = "MA", DefaultValidation = DefaultValidation.AT_LEAST_ONE_ASSET, Category = "Assets")] public static void MoveAssetToFolder( [CommandParameter(Help = "The name of the folder to which the selection will be moved", ForceAutoCompleteUsage = true, PreventDefaultValueUsage = true, AutoCompleteMethodName = "FolderAutoComplete")] string folderName) { foreach (var o in Selection.objects.Where(AssetDatabase.Contains)) { string objectName = AssetDatabase.GetAssetPath(o).GetAssetNameFromPath(true); string newFolder = folderName + "/" + objectName; if (AssetDatabase.ValidateMoveAsset(AssetDatabase.GetAssetPath(o), newFolder).IsNullOrEmpty()) { AssetDatabase.MoveAsset(AssetDatabase.GetAssetPath(o), newFolder); EditorGUIUtility.PingObject(o); } else { Debug.LogWarningFormat("Monkey Warning: The asset {0} " + "could not be moved to {1}.", o.name, newFolder); Debug.LogWarning(AssetDatabase.ValidateMoveAsset(AssetDatabase.GetAssetPath(o), folderName)); } } } [Command("Position In Circle", Help = "Positions all the selected objects in circle around the specified object (by default the first selected)", QuickName = "PIC", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT, Category = "Transform")] public static void PositionInCircle() { MonkeyEditorUtils.AddSceneCommand(new PositionInCircleSceneCommand( MonkeyEditorUtils.OrderedSelectedGameObjects.ToList() )); } [Command("Resolve Intersections", "Moves the selected objects so that they do not overlap each other", QuickName = "RI" , DefaultValidation = DefaultValidation.AT_LEAST_TWO_GAME_OBJECTS)] public static void ResolveIntersections(int iterations = 5) { var addedColliders = new List(); TransformUndo undo = new TransformUndo(); undo.Register(Selection.gameObjects.Convert(_ => _.transform).ToArray()); foreach (var gameObject in Selection.gameObjects) { var renderers = gameObject.GetComponentsInChildren(); if (renderers.Length == 0) continue; foreach (var renderer in renderers) { if (!renderer.GetComponent()) { var col = renderer.gameObject.AddComponent(); col.sharedMesh = renderer.GetComponent().sharedMesh; col.convex = true; addedColliders.Add(col); } } } for (int i = 0; i < iterations; i++) { EditorUtility.DisplayProgressBar("Resolving Intersection...", "MonKey is resolving the intersections", (float) i / iterations); foreach (var gameObject in Selection.gameObjects) { var renderers = gameObject.GetComponentsInChildren(); foreach (var otherObj in Selection.gameObjects) { var otherRenderers = otherObj.GetComponentsInChildren(); foreach (var renderer in renderers) { if (!renderer.gameObject.activeInHierarchy) continue; var collider = renderer.GetComponent(); foreach (var otherRenderer in otherRenderers) { if (otherRenderer == renderer) continue; if (!otherRenderer.gameObject.activeInHierarchy) continue; var otherCollider = otherRenderer.GetComponent(); if (Physics.ComputePenetration(collider, renderer.transform.position, renderer.transform.rotation, otherCollider, otherRenderer.transform.position, otherRenderer.transform.rotation, out var dir, out var distance)) { gameObject.transform.position += dir * distance; } } } } } EditorUtility.ClearProgressBar(); } foreach (var collider in addedColliders) { Object.DestroyImmediate(collider); } int id = MonkeyEditorUtils.CreateUndoGroup("MonKey - Resolve Intersections"); undo.RecordUndo(); Undo.CollapseUndoOperations(id); } public class PositionInCircleSceneCommand : TimedSceneCommand { public GameObject CircleCenter; public Axis CircleAxis; public float CircleRadius = 1; public float StartAngle = 0; public float EndAngle = 360; public bool LookAtCenter = false; public Vector3 UpVector; public Vector3 Offset; public bool useLocal; public List ObjectsToPosition; public PositionInCircleSceneCommand(List objectsToPosition) : base(0) { SceneCommandName = "Position In Circle"; ObjectsToPosition = objectsToPosition; TimeBetweenUpdate = 0.1f; EndAngle = 360; UpVector = Vector3.up; } public override void Update() { base.Update(); if (!CircleCenter || Mathf.Approximately(CircleRadius, 0)) return; float angle = StartAngle * Mathf.Deg2Rad; foreach (var gameObject in ObjectsToPosition) { if (gameObject == CircleCenter) continue; float xValue = CircleRadius * Mathf.Cos(angle); float yValue = CircleRadius * Mathf.Sin(angle); Vector3 radius = Vector3.zero; Vector3 offset = Offset; switch (CircleAxis) { case Axis.X: radius = new Vector3(0, yValue, xValue); break; case Axis.Y: radius = new Vector3(xValue, 0, yValue); break; case Axis.Z: radius = new Vector3(xValue, yValue, 0); break; case Axis.NONE: break; default: throw new ArgumentOutOfRangeException("circleAxis", CircleAxis, null); } offset += Offset; gameObject.transform.position = CircleCenter.transform.position + (useLocal ? CircleCenter.transform.TransformDirection(radius) : radius) + (useLocal ? CircleCenter.transform.TransformDirection(offset) : offset); if (LookAtCenter) { gameObject.transform.LookAt(CircleCenter.transform.position, (useLocal ? CircleCenter.transform.TransformDirection(UpVector) : UpVector)); } angle += (EndAngle * Mathf.Deg2Rad) / (ObjectsToPosition.Count); } } public override void DisplayParameters() { base.DisplayParameters(); DisplayObjectOption("Circle Center", ref CircleCenter); Enum en = CircleAxis; DisplayEnumOption("Circle Axis", ref en); DisplayBoolOption("Use Local Axes", ref useLocal); CircleAxis = (Axis) en; DisplayFloatOption("Circle Radius", ref CircleRadius); if (CircleRadius <= 0) { CircleRadius = 0.1f; } DisplayVectorOption("Offset", ref Offset); DisplayBoolOption("Look At Center", ref LookAtCenter); if (LookAtCenter) DisplayVectorOption("Look At Up Axis", ref UpVector); DisplayFloatOption("Start Angle", ref StartAngle); DisplayFloatOption("End Angle", ref EndAngle); DisplayObjectListOption("Objects To Position", ObjectsToPosition); } } public static GameObject DefaultCenter() { return MonkeyEditorUtils.OrderedSelectedGameObjects.ElementAt(0); } /* [Command("Move To Mouse Nearest Vertex", "Moves the selected objects to the closest vertex to the mouse")] public static void MoveToNearestVector() { MonkeyEditorUtils.AddSceneCommand(new VertexMoveSceneCommand(Selection.gameObjects)); } public class VertexMoveSceneCommand : ConfirmedCommand { private Transform[] toMove; private MethodInfo vertexFind; public VertexMoveSceneCommand(GameObject[] toMove) { this.toMove = toMove.Convert(_=>_.transform).ToArray(); vertexFind = typeof(HandleUtility).GetMethod("FindNearestVertex", BindingFlags.NonPublic | BindingFlags.Static); } public override void Update() { base.Update(); Vector3 nearestVertex=new Vector3(); //TODO not working: try to find a solution object[] parameters = { MonkeyEditorUtils.MousePosition, toMove, nearestVertex }; object result = vertexFind.Invoke(null, parameters); bool blResult = (bool)result; if (blResult) { nearestVertex = (Vector3)parameters[2]; toMove = toMove.Where(_ => _).ToArray(); foreach (var transform in toMove) { transform.position = nearestVertex; } } } } */ [Command("Move Mirror", "allows you to move selected objects mirrored relative to the distance of the active object to the centroid", Category = "Transform")] public static void MoveRelatedToCentroid() { MonkeyEditorUtils.AddSceneCommand(new MirrorRelatedToCentroidSceneCommand()); } public class MirrorRelatedToCentroidSceneCommand : TimedSceneCommand { public GameObject ReferenceObject; public List InfluencedObjects; public Vector3 Centroid; public Vector3 ReferenceDistance; public MirrorRelatedToCentroidSceneCommand() : base(-1) { InfluencedObjects = new List(Selection.gameObjects); Centroid = Vector3.zero; for (int i = 0; i < InfluencedObjects.Count; i++) { Centroid += InfluencedObjects[i].transform.position; } Centroid /= InfluencedObjects.Count; } public override void Update() { base.Update(); if (Selection.activeGameObject != ReferenceObject) { ReferenceObject = Selection.activeGameObject; ReferenceDistance = ReferenceObject.transform.position - Centroid; return; } if (!ReferenceObject) return; var newDistance = ReferenceObject.transform.position - Centroid; ReferenceDistance = newDistance; for (var index = 0; index < InfluencedObjects.Count; index++) { var gameObject = InfluencedObjects[index]; if (gameObject != ReferenceObject && gameObject) { var distance = -ReferenceDistance; gameObject.transform.position = Centroid + distance; } } } public override void OnSceneGUI() { base.OnSceneGUI(); Centroid = Handles.PositionHandle(Centroid, Quaternion.identity); Handles.Label(Centroid + Vector3.up * 0.1f, "Centroid"); foreach (var gameObject in InfluencedObjects) { Handles.DrawDottedLine(gameObject.transform.position, Centroid, 0.1f); } } public override void DisplayParameters() { base.DisplayParameters(); DisplayObjectListOption("Objects to Move", InfluencedObjects); } } public static AssetNameAutoComplete FolderAutoComplete() { return new AssetNameAutoComplete() {DirectoryMode = true}; } [MenuItem("Tools/MonKey Commander/Commands/Game Object/Move Up %&-")] [Command("Move Siblings Up", Category = "BFT", QuickName = "MU")] public static void MoveObjectUpInSiblings() { foreach (var o in Selection.gameObjects) { o.transform.SetSiblingIndex(Mathf.Max(0, o.transform.GetSiblingIndex() - 1)); } } [MenuItem("Tools/MonKey Commander/Commands/Game Object/Move Down %&=")] [Command("Move Siblings Down", Category = "BFT", QuickName = "MD")] public static void MoveObjectDownInSiblings() { foreach (var o in Selection.gameObjects) { o.transform.SetSiblingIndex(Mathf.Min( o.transform.parent ? o.transform.parent.childCount - 1 : o.scene.rootCount - 1, o.transform.GetSiblingIndex() + 1)); } } [Command("Move Pivot On Bounding Box", "Moves the pivot transform anywhere within the bounding box of the object", QuickName = "MPB", DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT)] public static void MovePivotOnBoundingBox() { if (Selection.activeGameObject.transform.childCount == 0) { Debug.LogWarning("MonKey Warning:" + " You are trying to move a a game object as a pivot " + "but it is not a pivot since it doesn't have children objects: " + "this command will not be executed"); return; } MonkeyEditorUtils.AddSceneCommand(new MovePivotSceneCommand()); } public class MovePivotSceneCommand : TimedSceneCommand { public enum BoundingBoxSide { TOP, BOTTOM, LEFT, RIGHT, FRONT, BACK } public enum BoundingBoxCorner { TOP_LEFT, TOP_RIGHT, TOP_CENTER, BOTTOM_LEFT, BOTTOM_RIGHT, BOTTOM_CENTER, CENTER_RIGHT, CENTER_LEFT, CENTER } public GameObject PivotToMove; private Vector3 previousPosition; // public BoundingBoxSide BoxSide; public float XPercent = 0.5f; public float YPercent = 0.5f; public float ZPercent = 0.5f; private Vector3 newPivot; private Bounds bounds; public MovePivotSceneCommand() : base(0) { SceneCommandName = "Move Pivot On BBox"; PivotToMove = Selection.activeGameObject; TimeBetweenUpdate = 0; } public override void Update() { base.Update(); if (!PivotToMove) return; bounds = GetMaxBounds(PivotToMove); var children = new List(PivotToMove.GetComponentsInChildren()); children.Remove(PivotToMove.transform); List previousPos = new List(children.Count); foreach (var transform in children) { previousPos.Add(transform.position); } newPivot = bounds.center + bounds.size.x * (XPercent - 0.5f) * Vector3.right + bounds.size.y * (YPercent - 0.5f) * Vector3.up + bounds.size.z * (ZPercent - 0.5f) * Vector3.forward; PivotToMove.transform.position = newPivot; for (var i = 0; i < children.Count; i++) { var transform = children[i]; transform.position = previousPos[i]; } } public override void OnSceneGUI() { base.OnSceneGUI(); Handles.color = Color.blue; Handles.SphereHandleCap(-1, newPivot, Quaternion.identity, .1f, EventType.Ignore); Handles.DrawWireCube(bounds.center, bounds.size); } public override void DisplayParameters() { base.DisplayParameters(); DisplayObjectOption("Pivot To Move", ref PivotToMove); DisplayFloatPercentOption("X Percent", ref XPercent); DisplayFloatPercentOption("Y Percent", ref YPercent); DisplayFloatPercentOption("Z Percent", ref ZPercent); } private Bounds GetMaxBounds(GameObject g) { var renderers = g.GetComponentsInChildren(); if (renderers.Length == 0) { return new Bounds(g.transform.position, Vector3.zero); } var first = renderers.First(); var b = first.bounds; foreach (Renderer r in renderers) { if (r == first) continue; b.Encapsulate(r.bounds); } return b; } } } } #endif