#if UNITY_EDITOR using MonKey.Editor.Internal; using MonKey.Extensions; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; namespace MonKey.Editor.Commands { public static class PhysicsUtilities { [Command("Set MeshColliders Convex", "Sets all the mesh colliders in the selected object and their children as convex", ValidationMethodName = "ObjectsSelected", QuickName = "SMC", Category = "Physics")] public static void SetMeshCollidersConvex() { foreach (var gameObject in Selection.gameObjects) { foreach (var collider in gameObject.GetComponentsInChildren()) { collider.convex = true; } } } [CommandValidation(DefaultValidationMessages.DEFAULT_SELECTED_GAMEOBJECTS)] public static bool ObjectsSelected() { return Selection.gameObjects.Length > 0; } #if UNITY_2017_1_OR_NEWER private static EditorPhysicsSceneCommand currentEditorPhysics; #endif [Command("Editor Physics", "Turn on the physics on all game objects in edit mode", DefaultValidation = DefaultValidation.IN_EDIT_MODE, QuickName = "EP", AlwaysShow = true , MenuItemLink = "ToggleEditorPhysics", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "Physics")] public static void ToggleEditorPhysics() { #if UNITY_2017_1_OR_NEWER if (currentEditorPhysics != null) { currentEditorPhysics.Stop(); return; } currentEditorPhysics = new EditorPhysicsSceneCommand(Object.FindObjectsOfType()); MonkeyEditorUtils.AddSceneCommand(currentEditorPhysics); #endif } #if UNITY_2017_1_OR_NEWER private static EditorPhysics2DSceneCommand currentEditorPhysics2D; #endif [Command("Editor Physics 2D", "Turn on the physics on all 2D game objects in edit mode", DefaultValidation = DefaultValidation.IN_EDIT_MODE, QuickName = "EP2", AlwaysShow = true, Category = "2D/Physics")] public static void ToggleEditorPhysics2D() { #if UNITY_2017_1_OR_NEWER if (currentEditorPhysics2D != null) { currentEditorPhysics2D.Stop(); return; } currentEditorPhysics2D = new EditorPhysics2DSceneCommand(Object.FindObjectsOfType()); MonkeyEditorUtils.AddSceneCommand(currentEditorPhysics2D); #endif } [Command("Editor Physics Selected", "Plays the physics on all selected game objects and children", DefaultValidation = DefaultValidation.IN_EDIT_MODE_AT_LEAST_ONE_GAME_OBJECT , QuickName = "EPS", MenuItemLink = "ToggleEditorPhysicsSelected", MenuItemLinkTypeOwner = "MonkeyMenuItems", Category = "Physics")] public static void ToggleEditorPhysicsSelected() { #if UNITY_2017_1_OR_NEWER if (currentEditorPhysics != null) { currentEditorPhysics.Stop(); return; } List bodies = new List(); List withoutBodies = new List(); foreach (var gameObject in Selection.gameObjects) { var rb = gameObject.GetComponentsInChildren(); if (rb.Length == 0) withoutBodies.Add(gameObject); bodies.AddRange(rb); } currentEditorPhysics = new EditorPhysicsSceneCommand(bodies.ToArray(), withoutBodies.ToArray()); MonkeyEditorUtils.AddSceneCommand(currentEditorPhysics); #endif } #if UNITY_2017_1_OR_NEWER public class EditorPhysicsSceneCommand : ConfirmedCommand { public bool ShowLabel; public bool PauseSim; public float Speed = 0.5f; private readonly List objectsWithAddedBody = new List(); private readonly List cachedBodies = new List(); private readonly List addedColliders = new List(); private readonly List excludedBodies = new List(); private readonly bool previousAutoSimulation; private readonly List previousPositionRotations = new List(); private struct PositionRotation { public readonly Vector3 Position; public readonly Quaternion Rotation; public PositionRotation(Vector3 position, Quaternion rotation) : this() { this.Position = position; this.Rotation = rotation; } } public EditorPhysicsSceneCommand(Rigidbody[] objects, params GameObject[] objectsWithoutBodies) : base() { ShowActionButton = false; ShowLabel = true; SceneCommandName = "Editor Physics Updater"; ConfirmationMode = ActionConfirmationMode.ENTER_AND_ESCAPE; cachedBodies.AddRange(objects); TimeBetweenUpdate = 0; foreach (var o in objectsWithoutBodies) { if (o.transform.GetAllParentTransforms() .Any(_ => objectsWithoutBodies.Any(ob => ob.transform == _))) continue; if (o.GetComponent()) { cachedBodies.Add(o.GetComponent()); continue; } if (o.GetComponentsInChildren().Length == 0) { var renders = o.GetComponentsInChildren(); foreach (var filter in renders) { var col = filter.gameObject.AddComponent(); col.sharedMesh = filter.sharedMesh; col.convex = true; addedColliders.Add(col); } } var rb = o.AddComponent(); objectsWithAddedBody.Add(o); cachedBodies.Add(rb); } Rigidbody[] allBodies = Object.FindObjectsOfType(); foreach (Rigidbody body in allBodies) { if (cachedBodies.Contains(body)) { previousPositionRotations.Add( new PositionRotation(body.position, body.rotation)); body.WakeUp(); } else { excludedBodies.Add(body); } } previousAutoSimulation = Physics.autoSimulation; Physics.autoSimulation = false; timer = 0; } public override string ConfirmationMessage() { return "Press ENTER to stop the physics simulation"; } public override void Stop() { base.Stop(); Physics.autoSimulation = previousAutoSimulation; currentEditorPhysics = null; foreach (Rigidbody body in cachedBodies) { if (!body) continue; body.velocity = Vector3.zero; body.angularVelocity = Vector3.zero; body.Sleep(); } foreach (var body in excludedBodies) { if (!body) continue; body.velocity = Vector3.zero; body.angularVelocity = Vector3.zero; body.Sleep(); } int i = 0; foreach (var body in cachedBodies) { if (!body) { i++; continue; } PositionRotation newPr = new PositionRotation(body.position, body.rotation); body.gameObject.transform.position = previousPositionRotations[i].Position; body.gameObject.transform.rotation = previousPositionRotations[i].Rotation; previousPositionRotations[i] = newPr; i++; } int id = MonkeyEditorUtils.CreateUndoGroup("Physics Simulation"); i = 0; foreach (var body in cachedBodies) { if (!body) { i++; continue; } Undo.RecordObject(body.transform, "position rotation"); body.gameObject.transform.position = previousPositionRotations[i].Position; body.gameObject.transform.rotation = previousPositionRotations[i].Rotation; i++; } foreach (var body in objectsWithAddedBody) { if (!body) continue; Object.DestroyImmediate(body.GetComponent()); } foreach (var collider in addedColliders) { Object.DestroyImmediate(collider); } Undo.CollapseUndoOperations(id); } public override void OnSceneGUI() { base.OnSceneGUI(); if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Space) ToggleSim(); if (!ShowLabel) return; Color previousColor = GUI.color; foreach (Rigidbody body in cachedBodies) { if (!body) continue; if (!body.IsSleeping()) { GUI.color = MonkeyStyle.Instance.SearchFieldTextColor; Handles.Label(body.transform.position, "Edit Mode Physics"); } } GUI.color = previousColor; } public override void DisplayParameters() { base.DisplayParameters(); DisplayBoolOption("Show Labels", ref ShowLabel); DisplayFloatOption("Sim Speed", ref Speed); DisplayIntOption("Max Frame Simulation Steps", ref maxSimSteps); maxSimSteps = Mathf.Max(1, maxSimSteps); Speed = Mathf.Max(Speed, 0.01f); DisplayButton("Toggle Simulation", ToggleSim); } private void ToggleSim() { PauseSim = !PauseSim; } private float timer; private int maxSimSteps = 10; public override void Update() { if (PauseSim) return; if (!MonkeyEditorUtils.CurrentSceneView) MonkeyEditorUtils.CurrentSceneView = SceneView.currentDrawingSceneView; MonkeyEditorUtils.CurrentSceneView.Focus(); foreach (var body in excludedBodies) { if (!body) continue; body.Sleep(); } timer += (float)MonkeyEditorUtils.EditorDeltaTime; int i = 0; while (timer >= Time.deltaTime) { timer -= Time.deltaTime; Physics.Simulate(Time.fixedDeltaTime * Speed); if (i > maxSimSteps) { break; } i++; } } } #endif #if UNITY_2017_1_OR_NEWER public class EditorPhysics2DSceneCommand : ConfirmedCommand { private readonly List objectsWithAddedBody = new List(); private readonly List cachedBodies = new List(); private readonly List excludedBodies = new List(); #if UNITY_2020_1_OR_NEWER private readonly SimulationMode2D previousAutoSimulation; #else private readonly bool previousAutoSimulation; #endif private readonly List previousPositionRotations = new List(); private struct PositionRotation { public readonly Vector3 Position; public readonly float Rotation; public PositionRotation(Vector3 position, float rotation) : this() { this.Position = position; this.Rotation = rotation; } } public EditorPhysics2DSceneCommand(Rigidbody2D[] objects, params GameObject[] objectsWithoutBodies) : base() { SceneCommandName = "Editor Physics Updater"; ConfirmationMode = ActionConfirmationMode.ENTER; cachedBodies.AddRange(objects); foreach (var o in objectsWithoutBodies) { if (o.transform.GetAllParentTransforms() .Any(_ => objectsWithoutBodies.Any(ob => ob.transform == _))) continue; if (o.GetComponent()) { cachedBodies.Add(o.GetComponent()); continue; } o.AddComponent(); objectsWithAddedBody.Add(o); } HideGUI = true; Rigidbody2D[] allBodies = Object.FindObjectsOfType(); foreach (Rigidbody2D body in allBodies) { if (!body) continue; if (cachedBodies.Contains(body)) { previousPositionRotations.Add( new PositionRotation(body.position, body.rotation)); body.WakeUp(); } else { excludedBodies.Add(body); } } #if UNITY_2020_1_OR_NEWER previousAutoSimulation = Physics2D.simulationMode; Physics2D.simulationMode = SimulationMode2D.Script; #else previousAutoSimulation = Physics2D.autoSimulation; Physics2D.autoSimulation = false; #endif timer = 0; } public override string ConfirmationMessage() { return "Press ENTER to stop the physics simulation"; } public override void Stop() { base.Stop(); foreach (Rigidbody2D body in cachedBodies) { if (!body) continue; body.velocity = Vector3.zero; body.angularVelocity = 0; body.Sleep(); } foreach (var body in excludedBodies) { if (!body) continue; body.velocity = Vector3.zero; body.angularVelocity = 0; body.Sleep(); } foreach (var body in objectsWithAddedBody) { if (!body) continue; Object.DestroyImmediate(body.GetComponent()); } int i = 0; foreach (var body in cachedBodies) { if (!body) { i++; continue; } PositionRotation newPr = new PositionRotation(body.position, body.rotation); body.gameObject.transform.position = previousPositionRotations[i].Position; body.rotation = previousPositionRotations[i].Rotation; previousPositionRotations[i] = newPr; i++; } int id = MonkeyEditorUtils.CreateUndoGroup("Physics Simulation"); i = 0; foreach (var body in cachedBodies) { if (!body) { i++; continue; } Undo.RecordObject(body.transform, "position rotation"); body.gameObject.transform.position = previousPositionRotations[i].Position; body.rotation = previousPositionRotations[i].Rotation; } Undo.CollapseUndoOperations(id); #if UNITY_2020_1_OR_NEWER Physics2D.simulationMode = previousAutoSimulation; currentEditorPhysics2D = null; #else Physics2D.autoSimulation = previousAutoSimulation; currentEditorPhysics2D = null; #endif } public override void OnSceneGUI() { base.OnSceneGUI(); Color previousColor = GUI.color; foreach (Rigidbody2D body in cachedBodies) { if (!body) continue; if (!body.IsSleeping()) { GUI.color = MonkeyStyle.Instance.SearchFieldTextColor; Handles.Label(body.transform.position, "Edit Mode Physics"); } } GUI.color = previousColor; } private float timer; public override void Update() { if (!MonkeyEditorUtils.CurrentSceneView) MonkeyEditorUtils.CurrentSceneView = SceneView.currentDrawingSceneView; MonkeyEditorUtils.CurrentSceneView.Focus(); foreach (var body in excludedBodies) { if (!body) continue; body.Sleep(); } timer += (float)MonkeyEditorUtils.EditorDeltaTime; while (timer >= Time.fixedDeltaTime) { timer -= Time.fixedDeltaTime; Physics2D.Simulate(Time.deltaTime); } } } #endif } } #endif