Net.Like.Xue.Tokyo/Assets/Plugins/MonKey Commander/Editor/MonKey/Sample Commands/MeasurementUtilities.cs

1208 lines
48 KiB
C#
Raw Normal View History

2024-11-03 16:42:23 +08:00
#if UNITY_EDITOR
using MonKey.Editor.Internal;
using MonKey.Extensions;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;
namespace MonKey.Editor.Commands
{
public static class MeasurementUtilities
{
[Command("Measure Distances Between",
"Displays the distances between the selected objects in the scene view", AlwaysShow = true,
DefaultValidation = DefaultValidation.AT_LEAST_TWO_TRANSFORMS, QuickName = "MCB",
Category = "Measurement")]
public static void ShowDistances(
[CommandParameter(Help = "The color of the debug on the scene view",
DefaultValueMethod = "DefaultDebugColor",
DefaultValueNameOverride = "Debug Green")]
Color debugColor)
{
MonkeyEditorUtils.
AddSceneCommand(new DistanceShowingSceneCommand(Selection.gameObjects, debugColor, 0));
}
public static Color DefaultDebugColor()
{
// return ColorUtils.HTMLColor("#228B22");
return Color.green;
}
public class DistanceShowingSceneCommand : TimedSceneCommand
{
private List<GameObject> objectsToMeasure;
private readonly Color color;
public DistanceShowingSceneCommand(GameObject[] objectsToMeasure, Color color,
float duration)
: base(duration)
{
SceneCommandName = "Measurement";
this.objectsToMeasure = new List<GameObject>(objectsToMeasure);
this.color = color;
}
public override void DisplayParameters()
{
base.DisplayParameters();
DisplayObjectListOption("Objects to Measure", objectsToMeasure);
}
public override void OnSceneGUI()
{
base.OnSceneGUI();
objectsToMeasure.RemoveAll(_ => !_);
for (int i = 0; i < objectsToMeasure.Count - 1; i++)
{
GameObject gameObject = objectsToMeasure[i];
for (int j = i + 1; j < objectsToMeasure.Count; j++)
{
GameObject otherObject = objectsToMeasure[j];
if (otherObject == gameObject)
continue;
Handles.color = color;
Handles.DrawDottedLine(gameObject.transform.position,
otherObject.transform.position, 10);
Vector3 distance = gameObject.transform.position - otherObject.transform.position;
Handles.Label((gameObject.transform.position + otherObject.transform.position) / 2,
"Distance: " + distance.magnitude, new GUIStyle()
{
normal = { textColor = color ,
background = MonkeyStyle.Instance.WindowBackgroundTex}
});
Handles.color = Color.white;
}
}
}
}
[Command("Measure Collision Distance", QuickName = "MCD",
Help = "Displays the distance of objects to any collision" +
" in the axis specified (default down)",
DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM,
Category = "Measurement")]
public static void MeasureCollisionDistance(
[CommandParameter(Help = "The color of the debug on the scene view",
DefaultValueMethod = "DefaultDebugColor",
DefaultValueNameOverride = "Debug Green")]
Color debugColor)
{
MonkeyEditorUtils.
AddSceneCommand(new DistanceAxisShowingSceneCommand(
Selection.gameObjects, debugColor, Vector3.down, false, 0));
}
[Command("Measure Collision Distance 2D", QuickName = "MCD2",
Help = "Displays the distance of 2d objects to any collision" +
" in the axis specified (default down)",
DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM,
Category = "Measurement")]
public static void MeasureCollisionDistance2D(
[CommandParameter(Help = "The color of the debug on the scene view",
DefaultValueMethod = "DefaultDebugColor",
DefaultValueNameOverride = "Debug Green")]
Color debugColor)
{
MonkeyEditorUtils.
AddSceneCommand(new DistanceAxisShowingSceneCommand2D(
Selection.gameObjects, debugColor, Vector3.down, false, 0));
}
[Command("Show Position Handles",
"Shows handles on all the specified objects (by default the selection)", QuickName = "SH",
Category = "Measurement")]
public static void ShowHandles(
[CommandParameter("The Objects to show the handle on",
DefaultValueMethod = "SelectionDefault",
ForceAutoCompleteUsage = true)]
GameObject[] objects)
{
MonkeyEditorUtils.AddSceneCommand(new TransformHandleShowingSceneCommand(objects));
}
private static GameObject[] SelectionDefault()
{
return Selection.gameObjects;
}
[Command("Measure Collision Distance All Axis",
"Displays the distance of an objects to any collision" +
" in all axis (global by default)", QuickName = "MCA",
DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM,
Category = "Measurement")]
public static void MeasureAllCollisionDistance(
[CommandParameter(Help = "if true, considers the local axis instead" +
" of the global one",
DefaultValueMethod = "DefaultLocal",DefaultValueNameOverride = "Global")]
AxisReference localAxisReference)
{
MonkeyEditorUtils.
AddSceneCommand(new DistanceAxisShowingSceneCommand(
Selection.gameObjects, Color.green.DarkerBrighter(-0.2f), Vector3.up, localAxisReference == AxisReference.LOCAL, 0));
MonkeyEditorUtils.
AddSceneCommand(new DistanceAxisShowingSceneCommand(
Selection.gameObjects, Color.green.DarkerBrighter(-0.2f), Vector3.down, localAxisReference == AxisReference.LOCAL, 0));
MonkeyEditorUtils.
AddSceneCommand(new DistanceAxisShowingSceneCommand(
Selection.gameObjects, Color.blue, Vector3.forward, localAxisReference == AxisReference.LOCAL, 0));
MonkeyEditorUtils.
AddSceneCommand(new DistanceAxisShowingSceneCommand(
Selection.gameObjects, Color.blue, Vector3.back, localAxisReference == AxisReference.LOCAL, 0));
MonkeyEditorUtils.
AddSceneCommand(new DistanceAxisShowingSceneCommand(
Selection.gameObjects, Color.red, Vector3.left, localAxisReference == AxisReference.LOCAL, 0));
MonkeyEditorUtils.
AddSceneCommand(new DistanceAxisShowingSceneCommand(
Selection.gameObjects, Color.red, Vector3.right, localAxisReference == AxisReference.LOCAL, 0));
}
private static AxisReference DefaultLocal()
{
return AxisReference.GLOBAL;
}
private static Vector3 DefaultAxis()
{
return Vector3.down;
}
public class TransformHandleShowingSceneCommand : TimedSceneCommand
{
private GameObject[] objectsToHandle;
public TransformHandleShowingSceneCommand(GameObject[] objectsToHandle) : base(0)
{
this.objectsToHandle = objectsToHandle;
SceneCommandName = "Handles";
HideGUI = true;
}
public override void OnSceneGUI()
{
base.OnSceneGUI();
foreach (GameObject gameObject in objectsToHandle)
{
if (!gameObject)
continue;
Vector3 position = Handles.DoPositionHandle(gameObject.transform.position,
gameObject.transform.localRotation);
Handles.Label(gameObject.transform.position + Vector3.up, gameObject.name);
if ((position - gameObject.transform.position).magnitude > Mathf.Epsilon)
{
Undo.RecordObject(gameObject.transform, "move");
}
gameObject.transform.position = position;
}
}
}
public class DistanceAxisShowingSceneCommand : TimedSceneCommand
{
private List<GameObject> objectsToMeasure;
private Vector3 axis;
private bool local;
private Color color;
public DistanceAxisShowingSceneCommand(GameObject[] objectsToMeasure,
Color color, Vector3 axis, bool local, float duration)
: base(duration)
{
SceneCommandName = "Distance Measurement";
this.objectsToMeasure = objectsToMeasure.ToList();
this.color = color;
this.local = local;
this.axis = axis;
}
public override void DisplayParameters()
{
base.DisplayParameters();
DisplayVectorOption("Direction", ref axis);
DisplayBoolOption("Local Axis", ref local);
DisplayObjectListOption("Objects To Measure", objectsToMeasure);
}
public override void OnSceneGUI()
{
base.OnSceneGUI();
objectsToMeasure = objectsToMeasure.Where(_ => _).ToList();
for (int i = 0; i < objectsToMeasure.Count; i++)
{
GameObject gameObject = objectsToMeasure[i];
if (!gameObject)
return;
Ray ray = new Ray(gameObject.transform.position,
(local) ? gameObject.transform.TransformDirection(axis) : axis);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
Handles.color = color;
#if UNITY_2017_1_OR_NEWER
Handles.zTest = CompareFunction.LessEqual;
#endif
Handles.DrawDottedLine(gameObject.transform.position,
hit.point, 5);
Handles.DrawWireDisc(hit.point, hit.normal, 0.5f);
#if UNITY_2017_1_OR_NEWER
Handles.zTest = CompareFunction.Always;
#endif
Vector3 distance = gameObject.transform.position - hit.point;
Handles.Label((gameObject.transform.position + hit.point) / 2,
"Distance: " + distance.magnitude, new GUIStyle()
{
margin = new RectOffset(2, 2, 2, 2),
normal =
{
textColor = color,
background = MonkeyStyle.Instance.WindowBackgroundTex
}
});
Handles.color = color.Inverted();
#if UNITY_2017_1_OR_NEWER
Handles.zTest = CompareFunction.Greater;
#endif
Handles.DrawDottedLine(gameObject.transform.position,
hit.point, 5);
Handles.DrawWireDisc(hit.point + hit.normal * 0.1f, hit.normal, 0.5f);
Handles.color = Color.white;
}
}
}
}
public class DistanceAxisShowingSceneCommand2D : TimedSceneCommand
{
private List<GameObject> objectsToMeasure;
private Vector2 axis;
private bool local;
private Color color;
public DistanceAxisShowingSceneCommand2D(GameObject[] objectsToMeasure,
Color color, Vector3 axis, bool local, float duration)
: base(duration)
{
SceneCommandName = "Distance Measurement";
this.objectsToMeasure = objectsToMeasure.ToList();
this.color = color;
this.local = local;
this.axis = axis;
}
public override void DisplayParameters()
{
base.DisplayParameters();
DisplayVector2Option("Direction", ref axis);
DisplayBoolOption("Local Axis", ref local);
DisplayObjectListOption("Objects To Measure", objectsToMeasure);
}
public override void OnSceneGUI()
{
base.OnSceneGUI();
objectsToMeasure = objectsToMeasure.Where(_ => _).ToList();
for (int i = 0; i < objectsToMeasure.Count; i++)
{
GameObject gameObject = objectsToMeasure[i];
if (!gameObject)
return;
RaycastHit2D hit = Physics2D.Raycast(gameObject.transform.position,
(local) ? (Vector2)gameObject.transform.TransformDirection(axis) : axis);
if (hit.collider != null)
{
Handles.color = color;
#if UNITY_2017_1_OR_NEWER
Handles.zTest = CompareFunction.LessEqual;
#endif
Handles.DrawDottedLine(gameObject.transform.position,
hit.point, 5);
Handles.DrawWireDisc(hit.point, hit.normal, 0.5f);
#if UNITY_2017_1_OR_NEWER
Handles.zTest = CompareFunction.Always;
#endif
Vector2 distance = (Vector2)gameObject.transform.position - hit.point;
Handles.Label(((Vector2)gameObject.transform.position + hit.point) / 2,
"Distance: " + distance.magnitude, new GUIStyle()
{
margin = new RectOffset(2, 2, 2, 2),
normal =
{
textColor = color,
background = MonkeyStyle.Instance.WindowBackgroundTex
}
});
Handles.color = color.Inverted();
#if UNITY_2017_1_OR_NEWER
Handles.zTest = CompareFunction.Greater;
#endif
Handles.DrawDottedLine(gameObject.transform.position,
hit.point, 5);
Handles.DrawWireDisc(hit.point + hit.normal * 0.1f, hit.normal, 0.5f);
Handles.color = Color.white;
}
}
}
}
[Command("Count Selected",
"Counts the amount of objects selected and outputs the result in the console",
QuickName = "COS",
Category = "Measurement")]
public static void CountSelectedObject()
{
Debug.LogFormat("MonKey Counted:".Colored(MonkeyStyle.Instance.SelectedResultFieldColor).Bold() +
"\n {0} Objects Selected" +
", {1} GameObjects Selected " +
", {2} Transforms Selected", Selection.objects.Length,
Selection.gameObjects.Length,
Selection.transforms.Length);
}
[Command("Randomize Scale",
"Randomizes the local scale of the selected objects " +
"within a specified range (by default .8 to 1.2)", QuickName = "RAS",
DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM,
IgnoreHotKeyConflict = true,
MenuItemLink = "RandomizeScale", MenuItemLinkTypeOwner = "MonkeyMenuItems",
Category = "Transform/Set")]
public static void RandomizeScale()
{
MonkeyEditorUtils.AddSceneCommand(
new InteractiveRandomScale(Selection.gameObjects));
}
[Command("Set Local Scale", "Set the specified scale on the selected objects",
DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM, QuickName = "SLS",
MenuItemLink = "SetLocalScale", MenuItemLinkTypeOwner = "MonkeyMenuItems",
Category = "Transform/Set")]
public static void SetScale(
[CommandParameter(Help = "The local scale to set on the selected objects")]
Vector3 scale)
{
foreach (GameObject gameObject in Selection.gameObjects)
{
gameObject.transform.localScale = scale;
}
}
[Command("Set Local Rotation", "Set the specified rotation on the selected objects",
DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM, QuickName = "SLR",
MenuItemLink = "SetLocalRotation", MenuItemLinkTypeOwner = "MonkeyMenuItems",
Category = "Transform/Set")]
public static void SetRotation(
[CommandParameter(Help = "The local rotation to set on the selected objects")]
Vector3 rotation)
{
foreach (GameObject gameObject in Selection.gameObjects)
{
gameObject.transform.localRotation = Quaternion.Euler(rotation);
}
}
[Command("Set Local Position", "Set the specified rotation on the selected objects",
DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM, QuickName = "SLP",
MenuItemLink = "SetLocalPosition", MenuItemLinkTypeOwner = "MonkeyMenuItems",
Category = "Transform/Set")]
public static void SetPosition(
[CommandParameter(Help = "The local position to set on the selected objects")]
Vector3 position)
{
int id = MonkeyEditorUtils.CreateUndoGroup("MonKey : Local Position Set");
foreach (GameObject gameObject in Selection.gameObjects)
{
Undo.RecordObject(gameObject.transform, " tr");
gameObject.transform.localPosition = position;
}
Undo.CollapseUndoOperations(id);
}
private static Vector3 OneVector()
{
return Vector3.one;
}
[Command("Randomize Rotation",
"Randomizes the local rotation of the selected objects " +
"within a specified range (by default 0 to 360)", QuickName = "RAR",
DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM,
IgnoreHotKeyConflict = true,
MenuItemLink = "RandomizeRotation", MenuItemLinkTypeOwner = "MonkeyMenuItems",
Category = "Transform/Set")]
public static void RandomizeRotation()
{
MonkeyEditorUtils.AddSceneCommand(
new InteractiveRandomRotation(Selection.gameObjects));
}
[Command("Randomize Position",
"Randomizes the local position of the selected objects " +
"within a specified range (by default -1 to 1)", QuickName = "RAP",
IgnoreHotKeyConflict = true,
DefaultValidation = DefaultValidation.AT_LEAST_ONE_TRANSFORM,
MenuItemLink = "RandomizePosition", MenuItemLinkTypeOwner = "MonkeyMenuItems",
Category = "Transform/Set")]
public static void RandomizePosition()
{
MonkeyEditorUtils.AddSceneCommand(
new InteractiveRandomPosition(Selection.gameObjects));
}
public static Vector2 DefaultScaleRange()
{
return new Vector2(.8f, 1.2f);
}
public static Vector2[] RandomRotationRangeDefaultValue()
{
return new Vector2[] { new Vector2(-180, 180f) };
}
public static CommandParameterAutoComplete<Vector2> RandomRotationRangeAutoComplete()
{
return new CommandParameterAutoComplete<Vector2>().
AddValue("-180 to 180", new Vector2(-180, 180f))
.AddValue("0 to 180", new Vector2(0, 180))
.AddValue("-90 to +90", new Vector2(-90, 90))
.AddValue("0 to 360", new Vector2(0, 360));
}
public static CommandParameterAutoComplete<Vector2> RandomScaleRangeAutoComplete()
{
return new CommandParameterAutoComplete<Vector2>().AddValue("0 to 1", Vector2.up)
.AddValue("1 to 2", new Vector2(1, 2))
.AddValue("1 to 5", new Vector2(1, 5))
.AddValue("0.5 to 1.5", new Vector2(.5f, 1.5f))
.AddValue("0.8 to 1.2", new Vector2(.8f, 1.2f));
}
public static CommandParameterAutoComplete<Vector2> RandomPositionRangeAutoComplete()
{
return new CommandParameterAutoComplete<Vector2>()
.AddValue("-0.25 to 0.25", new Vector2(-.25f, .25f))
.AddValue("-0.5 to 0.5", new Vector2(-.5f, .5f))
.AddValue("-1 to 1", new Vector2(-1, 1))
.AddValue("-2 to 2", new Vector2(-2, 2))
.AddValue("-5 to 5", new Vector2(-5, 5))
.AddValue("-10 to 10", new Vector2(-10, 10))
.AddValue("-50 to 50", new Vector2(-50, 50));
}
public class InteractiveRandomScale : InteractiveCommand
{
private Vector2 scaleRandom;
private List<GameObject> toRandomize;
public float Multiplier = 1;
public InteractiveRandomScale(GameObject[] toRandomize)
{
ConfirmationMode = ActionConfirmationMode.ESCAPE;
SceneCommandName = "Transform Randomization";
ActionOnSpace = "to randomize transforms";
this.scaleRandom = new Vector2(0.8f, 1.1f);
this.toRandomize = toRandomize.ToList();
}
public override void DisplayParameters()
{
base.DisplayParameters();
float min = scaleRandom.x;
float max = scaleRandom.y;
DisplayFloatOption("Min Scale", ref min);
DisplayFloatOption("Max Scale", ref max);
scaleRandom = new Vector2(min, max);
DisplayFloatOption("Multiplier", ref Multiplier);
DisplayObjectListOption("Objects To Randomize", toRandomize);
}
public override void ApplyFunction()
{
OnSpaceDownAction();
}
protected override void OnSpaceDownAction()
{
base.OnSpaceDownAction();
int undoID = MonkeyEditorUtils.CreateUndoGroup("Randomization");
toRandomize.RemoveAll(_ => !_);
foreach (GameObject gameObject in toRandomize)
{
Undo.RecordObject(gameObject.transform, "Randomization trans");
gameObject.transform.localScale = Vector3.one * (Random.Range(scaleRandom.x, scaleRandom.y) * Multiplier);
}
Undo.CollapseUndoOperations(undoID);
}
}
public class InteractiveRandomRotation : InteractiveCommand
{
private readonly Axis[] randomAxes = { Axis.X, Axis.Y, Axis.Z };
private readonly Vector2[] randomPerAxis;
private List<GameObject> toRandomize;
private readonly Quaternion[] previousAngles;
public float Multiplier = 1f;
public InteractiveRandomRotation(GameObject[] toRandomize)
{
ConfirmationMode = ActionConfirmationMode.ESCAPE;
SceneCommandName = "Transform Randomization";
ActionOnSpace = "to randomize transforms";
this.randomPerAxis = new Vector2[] { new Vector2(0, 0), new Vector2(-180, 180), new Vector2(0, 0) };
this.toRandomize = new List<GameObject>(toRandomize);
previousAngles = toRandomize.Convert(_ => _.transform.localRotation).ToArray();
}
public override void DisplayParameters()
{
base.DisplayParameters();
DisplayVector2Option("X Axis Random Range", ref randomPerAxis[0]);
DisplayVector2Option("Y Axis Random Range", ref randomPerAxis[1]);
DisplayVector2Option("Z Axis Random Range", ref randomPerAxis[2]);
DisplayFloatOption("Multiplier", ref Multiplier);
DisplayObjectListOption("Objects To Randomize", toRandomize);
}
public override void ApplyFunction()
{
OnSpaceDownAction();
}
protected override void OnSpaceDownAction()
{
base.OnSpaceDownAction();
int undoID = MonkeyEditorUtils.CreateUndoGroup("Randomization");
int objectID = 0;
toRandomize.RemoveAll(_ => !_);
foreach (GameObject gameObject in toRandomize)
{
Undo.RecordObject(gameObject.transform, "Randomization trans");
if (randomPerAxis != null && randomPerAxis.Length > 0)
{
int i = 0;
gameObject.transform.localRotation = previousAngles[objectID];
Vector3 previousUp = gameObject.transform.up;
Vector3 previousRight = gameObject.transform.right;
Vector3 previousForward = gameObject.transform.forward;
foreach (Axis axis in randomAxes)
{
int j = i;
if (i >= randomPerAxis.Length)
{
j = 0;
}
float angle = Random.Range(randomPerAxis[j].x, randomPerAxis[j].y) * Multiplier;
switch (axis)
{
case Axis.X:
gameObject.transform.Rotate(previousRight, angle, Space.World);
break;
case Axis.Y:
gameObject.transform.Rotate(previousUp, angle, Space.World);
break;
case Axis.Z:
gameObject.transform.Rotate(previousForward, angle, Space.World);
break;
}
i++;
}
}
objectID++;
}
Undo.CollapseUndoOperations(undoID);
}
}
public class InteractiveRandomPosition : InteractiveCommand
{
private readonly Axis[] randomAxes;
private readonly Vector2[] randomPerAxis;
private List<GameObject> toRandomize;
private readonly Vector3[] previousPositions;
public float Multiplier = 1f;
public bool LocalSpace = true;
public InteractiveRandomPosition(GameObject[] toRandomize)
{
ConfirmationMode = ActionConfirmationMode.ESCAPE;
SceneCommandName = "Transform Randomization";
ActionOnSpace = "to randomize transforms";
randomAxes = new[] { Axis.X, Axis.Y, Axis.Z };
this.randomPerAxis = new[] { new Vector2(-0.1f, 0.1f), new Vector2(-0.1f, 0.1f), new Vector2(-0.1f, 0.1f) }; ;
this.toRandomize = new List<GameObject>(toRandomize);
LocalSpace = true;
previousPositions = toRandomize.Convert(_ => _.transform.position).ToArray();
}
public override void DisplayParameters()
{
base.DisplayParameters();
DisplayBoolOption("Local Space", ref LocalSpace);
DisplayVector2Option("X Axis Random Range", ref randomPerAxis[0]);
DisplayVector2Option("Y Axis Random Range", ref randomPerAxis[1]);
DisplayVector2Option("Z Axis Random Range", ref randomPerAxis[2]);
DisplayFloatOption("Multiplier", ref Multiplier);
DisplayObjectListOption("Objects To Randomize", toRandomize);
}
public override void ApplyFunction()
{
OnSpaceDownAction();
}
protected override void OnSpaceDownAction()
{
base.OnSpaceDownAction();
int undoID = MonkeyEditorUtils.CreateUndoGroup("Randomization");
int objectID = 0;
toRandomize.RemoveAll(_ => !_);
foreach (GameObject gameObject in toRandomize)
{
Undo.RecordObject(gameObject.transform, "Randomization trans");
if (randomPerAxis != null && randomPerAxis.Length > 0)
{
int i = 0;
gameObject.transform.position = previousPositions[objectID];
Vector3 previousUp = LocalSpace? gameObject.transform.up:Vector3.up;
Vector3 previousRight = LocalSpace ? gameObject.transform.right : Vector3.right;
Vector3 previousForward = LocalSpace ? gameObject.transform.forward : Vector3.forward;
foreach (Axis axis in randomAxes)
{
int j = i;
if (i >= randomPerAxis.Length)
{
j = 0;
}
float positionJitter = Random.Range(randomPerAxis[j].x, randomPerAxis[j].y)*Multiplier;
switch (axis)
{
case Axis.X:
gameObject.transform.position =
gameObject.transform.position + positionJitter * previousRight;
break;
case Axis.Y:
gameObject.transform.position =
gameObject.transform.position + positionJitter * previousUp;
break;
case Axis.Z:
gameObject.transform.position =
gameObject.transform.position + positionJitter * previousForward;
break;
}
i++;
}
}
objectID++;
}
Undo.CollapseUndoOperations(undoID);
}
}
public class InteractiveRandomScaleRotationPosition : InteractiveCommand
{
public bool randomizeScale = false;
private Vector2 scaleRandom;
private readonly Axis[] randomAxes;
private readonly Vector2[] randomPerAxis;
private List<GameObject> toRandomize;
private readonly Quaternion[] previousAngles;
private readonly Vector3[] previousPositions;
private bool randomizePosition;
public float Multiplier = 1f;
public InteractiveRandomScaleRotationPosition(GameObject[] toRandomize, Vector2 scaleRandom,
Vector2[] randomPerAxis, bool randomizePosition = false)
{
ConfirmationMode = ActionConfirmationMode.ESCAPE;
SceneCommandName = "Transform Randomization";
ActionOnSpace = "to randomize transforms";
this.scaleRandom = scaleRandom;
if (scaleRandom != Vector2.zero)
randomizeScale = true;
this.randomAxes = new Axis[] { Axis.X, Axis.Y, Axis.Z };
this.randomPerAxis = randomPerAxis;
this.toRandomize = new List<GameObject>(toRandomize);
this.randomizePosition = randomizePosition;
previousAngles = toRandomize.Convert(_ => _.transform.localRotation).ToArray();
previousPositions = toRandomize.Convert(_ => _.transform.position).ToArray();
}
public override void DisplayParameters()
{
base.DisplayParameters();
float min = scaleRandom.x;
float max = scaleRandom.y;
DisplayFloatOption("Min Scale", ref min);
DisplayFloatOption("Max Scale", ref max);
scaleRandom = new Vector2(min, max);
DisplayFloatOption("Multiplier", ref Multiplier);
DisplayObjectListOption("Objects To Randomize", toRandomize);
}
protected override void OnSpaceDownAction()
{
base.OnSpaceDownAction();
int undoID = MonkeyEditorUtils.CreateUndoGroup("Randomization");
int objectID = 0;
toRandomize.RemoveAll(_ => !_);
foreach (GameObject gameObject in toRandomize)
{
Undo.RecordObject(gameObject.transform, "Randomization trans");
if (randomizeScale)
gameObject.transform.localScale = Vector3.one *
Random.Range(scaleRandom.x, scaleRandom.y) * Multiplier;
if (randomizePosition)
{
if (randomPerAxis != null && randomPerAxis.Length > 0)
{
int i = 0;
gameObject.transform.position = previousPositions[objectID];
Vector3 previousUp = gameObject.transform.up;
Vector3 previousRight = gameObject.transform.right;
Vector3 previousForward = gameObject.transform.forward;
foreach (Axis axis in randomAxes)
{
int j = i;
if (i >= randomPerAxis.Length)
{
j = 0;
}
float positionJitter = Random.Range(randomPerAxis[j].x, randomPerAxis[j].y) * Multiplier;
switch (axis)
{
case Axis.X:
gameObject.transform.position =
gameObject.transform.position + positionJitter * previousRight;
break;
case Axis.Y:
gameObject.transform.position =
gameObject.transform.position + positionJitter * previousUp;
break;
case Axis.Z:
gameObject.transform.position =
gameObject.transform.position + positionJitter * previousForward;
break;
}
i++;
}
}
}
else
{
if (randomPerAxis != null && randomPerAxis.Length > 0)
{
int i = 0;
gameObject.transform.localRotation = previousAngles[objectID];
Vector3 previousUp = gameObject.transform.up;
Vector3 previousRight = gameObject.transform.right;
Vector3 previousForward = gameObject.transform.forward;
foreach (Axis axis in randomAxes)
{
int j = i;
if (i >= randomPerAxis.Length)
{
j = 0;
}
float angle = Random.Range(randomPerAxis[j].x, randomPerAxis[j].y);
switch (axis)
{
case Axis.X:
gameObject.transform.Rotate(previousRight, angle, Space.World);
break;
case Axis.Y:
gameObject.transform.Rotate(previousUp, angle, Space.World);
break;
case Axis.Z:
gameObject.transform.Rotate(previousForward, angle, Space.World);
break;
}
i++;
}
}
}
objectID++;
}
Undo.CollapseUndoOperations(undoID);
}
}
[Command("Spread And Align Between", QuickName = "SA",
Help = "Spreads equally and aligns selected objects between two others",
DefaultValidation = DefaultValidation.AT_LEAST_TWO_GAME_OBJECTS,
Category = "Transform/Align")]
public static void SpreadBetween()
{
GameObject[] selected =
MonkeyEditorUtils.OrderedSelectedGameObjects.ToArray();
MonkeyEditorUtils.AddSceneCommand(new SpreadBetweenSceneCommand(null, null, selected));
}
public static GameObject FirstSelected()
{
return MonkeyEditorUtils.OrderedSelectedGameObjects.First();
}
public static GameObject SecondSelected()
{
return MonkeyEditorUtils.OrderedSelectedGameObjects.ElementAt(1);
}
public class SpreadBetweenSceneCommand : TimedSceneCommand
{
private GameObject first;
private GameObject second;
private List<GameObject> selected;
private List<Vector3> previousPos;
private List<Quaternion> previousRot;
public SpreadBetweenSceneCommand(GameObject first, GameObject second,
GameObject[] selected) : base(0)
{
SceneCommandName = "Spread Between";
this.first = first;
this.second = second;
this.selected = selected.ToList();
previousPos = selected.Convert(_ => _.transform.position).ToList();
previousRot = selected.Convert(_ => _.transform.rotation).ToList();
}
public override void DisplayParameters()
{
DisplayObjectOption("Start Object", ref first);
DisplayObjectOption("End Object", ref second);
int id = DisplayObjectListOption("Objects To Spread", selected);
if (id != -1)
{
if (previousPos.Count <= id)
{
previousPos.Add(Vector3.zero);
previousRot.Add(Quaternion.identity);
}
previousPos[id] = selected[id].transform.position;
previousRot[id] = selected[id].transform.rotation;
}
}
public override void Update()
{
base.Update();
if (!first || !second)
return;
Vector3 direction = second.transform.position - first.transform.position;
float distance = direction.magnitude / (selected.Count + 1);
Vector3 currentPosition = first.transform.position + distance * direction.normalized;
selected = selected.Where(_ => _).ToList();
foreach (var gameObject in selected)
{
gameObject.transform.position = currentPosition;
currentPosition += distance * direction.normalized;
}
}
public override void OnSceneGUI()
{
base.OnSceneGUI();
if (!first || !second)
return;
Handles.DrawDottedLine(first.transform.position,
second.transform.position, 1f);
}
public override void Stop()
{
base.Stop();
Vector3[] newPositions = selected.Convert(_ => _.transform.position).ToArray();
Quaternion[] newRotations = selected.Convert(_ => _.transform.rotation).ToArray();
for (int i = 0; i < previousRot.Count; i++)
{
selected[i].transform.position = previousPos[i];
selected[i].transform.rotation = previousRot[i];
}
int undoIndex = MonkeyEditorUtils.CreateUndoGroup("Spread Between");
for (int i = 0; i < selected.Count; i++)
{
Undo.RecordObject(selected[i].transform, "Applying new position");
selected[i].transform.position = newPositions[i];
selected[i].transform.rotation = newRotations[i];
}
Undo.CollapseUndoOperations(undoIndex);
}
}
[Command("Visualize Colliders", "Adds temporary visualizations to all the colliders selected",
QuickName = "VC",
DefaultValidation = DefaultValidation.AT_LEAST_ONE_GAME_OBJECT,
Category = "Physics")]
public static void VisualizeColliders(
[CommandParameter("The material to set for the debug meshes (by default the default material)"
,DefaultValueMethod = "DefaultMat")]
Material mat)
{
List<GameObject> objToDebug = new List<GameObject>();
foreach (var o in Selection.gameObjects)
{
var cols = o.GetComponentsInChildren<Collider>();
objToDebug.AddRange(cols.Convert(_ => _.gameObject));
}
MonkeyEditorUtils.AddSceneCommand(new TemporaryMeshSceneCommand(objToDebug.ToArray(), mat, 0));
}
private static Material DefaultMat()
{
return AssetDatabase.GetBuiltinExtraResource<Material>("Default-Diffuse.mat");
}
public class TemporaryMeshSceneCommand : TimedSceneCommand
{
private readonly List<GameObject> addedRenderers = new List<GameObject>();
private readonly Dictionary<BoxCollider, GameObject> transformsPerBoxCollider
= new Dictionary<BoxCollider, GameObject>();
private readonly Dictionary<SphereCollider, GameObject> transformsPerSphereCollider
= new Dictionary<SphereCollider, GameObject>();
private readonly Dictionary<CapsuleCollider, GameObject> transformsPerCapsuleCollider
= new Dictionary<CapsuleCollider, GameObject>();
public TemporaryMeshSceneCommand(GameObject[] objects, Material mat, float duration) : base(duration)
{
TimeBetweenUpdate = 0.2;
SceneCommandName = "Collider Visualization";
foreach (GameObject o in objects)
{
Collider col = o.GetComponent<Collider>();
GameObject renderer = null;
//ugly type checking but heh, it works.
if (col is BoxCollider box)
{
renderer = GameObject.CreatePrimitive(PrimitiveType.Cube);
renderer.transform.SetParent(o.transform, false);
UpdateBoxScale(renderer, box);
transformsPerBoxCollider.Add(box, renderer);
}
else if (col is SphereCollider sphere)
{
renderer = GameObject.CreatePrimitive(PrimitiveType.Sphere);
renderer.transform.SetParent(o.transform, false);
UpdateSphereScale(renderer, sphere);
transformsPerSphereCollider.Add(sphere, renderer);
}
else if (col is CapsuleCollider caps)
{
renderer = GameObject.CreatePrimitive(PrimitiveType.Capsule);
renderer.transform.SetParent(o.transform, false);
UpdateCapsScale(renderer, caps);
transformsPerCapsuleCollider.Add(caps, renderer);
}
else if (col is MeshCollider mesh)
{
renderer = new GameObject();
MeshFilter filter = renderer.AddComponent<MeshFilter>();
filter.sharedMesh = mesh.sharedMesh;
renderer.AddComponent<MeshRenderer>();
}
if (renderer != null)
{
renderer.name = "MonKey Debug " + renderer.name;
renderer.GetComponent<MeshRenderer>().material = mat;
Object.DestroyImmediate(renderer.GetComponent<Collider>());
addedRenderers.Add(renderer);
}
}
}
private static void UpdateCapsScale(GameObject renderer, CapsuleCollider capsuleCollider)
{
renderer.transform.localScale = capsuleCollider.radius * 2 * Vector3.one;
//for capsules the mesh is going to stretch:
//could be possible to generate a proper mesh, but to isolated of a problem to care
renderer.transform.localScale += Vector3.up * (capsuleCollider.height - 2);
}
private static void UpdateSphereScale(GameObject renderer, SphereCollider sphereCollider)
{
renderer.transform.localScale = sphereCollider.radius * 2 * Vector3.one;
}
private static void UpdateBoxScale(GameObject renderer, BoxCollider boxCollider)
{
renderer.transform.localScale = boxCollider.size;
}
public override void Update()
{
base.Update();
foreach (var tpb in transformsPerBoxCollider)
{
UpdateBoxScale(tpb.Value, tpb.Key);
}
foreach (var tps in transformsPerSphereCollider)
{
UpdateSphereScale(tps.Value, tps.Key);
}
foreach (var tpc in transformsPerCapsuleCollider)
{
UpdateCapsScale(tpc.Value, tpc.Key);
}
}
public override void Stop()
{
foreach (var renderer in addedRenderers)
{
Object.DestroyImmediate(renderer);
}
base.Stop();
}
}
}
}
#endif