From 454fcccd6c3964960c01809ca7b3070520e0ad4a Mon Sep 17 00:00:00 2001 From: Santiago Ariel Mansilla <56666653+Gann4Life@users.noreply.github.com> Date: Tue, 11 Jul 2023 08:44:24 -0300 Subject: [PATCH 1/3] Handles upgrade, refactor Joints and other elements can be dragged with angle and position handles, a refactor has been made to expand on this later. --- .../Editor/RagdollFactoryEditor.SceneGUI.cs | 235 +------ .../Scripts/Editor/RagdollFactoryEditor.cs | 17 +- .../Ragdoll Factory/Scripts/RagdollFactory.cs | 628 +----------------- .../Ragdoll Factory/Scripts/States.meta | 3 + .../States/BoxColliderComponentState.cs | 132 ++++ .../States/BoxColliderComponentState.cs.meta | 3 + .../States/CapsuleColliderComponentState.cs | 134 ++++ .../CapsuleColliderComponentState.cs.meta | 3 + .../States/ConfigurableJointComponentState.cs | 275 ++++++++ .../ConfigurableJointComponentState.cs.meta | 3 + .../Scripts/States/RFComponentState.cs | 77 +++ .../Scripts/States/RFComponentState.cs.meta | 3 + .../Scripts/States/RigidbodyComponentState.cs | 91 +++ .../States/RigidbodyComponentState.cs.meta | 3 + 14 files changed, 795 insertions(+), 812 deletions(-) create mode 100644 Gann4Games/Ragdoll Factory/Scripts/States.meta create mode 100644 Gann4Games/Ragdoll Factory/Scripts/States/BoxColliderComponentState.cs create mode 100644 Gann4Games/Ragdoll Factory/Scripts/States/BoxColliderComponentState.cs.meta create mode 100644 Gann4Games/Ragdoll Factory/Scripts/States/CapsuleColliderComponentState.cs create mode 100644 Gann4Games/Ragdoll Factory/Scripts/States/CapsuleColliderComponentState.cs.meta create mode 100644 Gann4Games/Ragdoll Factory/Scripts/States/ConfigurableJointComponentState.cs create mode 100644 Gann4Games/Ragdoll Factory/Scripts/States/ConfigurableJointComponentState.cs.meta create mode 100644 Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs create mode 100644 Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs.meta create mode 100644 Gann4Games/Ragdoll Factory/Scripts/States/RigidbodyComponentState.cs create mode 100644 Gann4Games/Ragdoll Factory/Scripts/States/RigidbodyComponentState.cs.meta diff --git a/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.SceneGUI.cs b/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.SceneGUI.cs index 7fdb970..0b8842a 100644 --- a/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.SceneGUI.cs +++ b/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.SceneGUI.cs @@ -1,4 +1,5 @@ -using UnityEditor; +using Gann4Games.RagdollFactory.States; +using UnityEditor; using UnityEngine; using UnityEngine.UIElements; @@ -6,238 +7,68 @@ namespace Gann4Games.RagdollFactory { public partial class RagdollFactoryEditor { - private static Vector3 MousePosition => Event.current.mousePosition; - public static Ray MouseRay => HandleUtility.GUIPointToWorldRay(MousePosition); private void OnSceneGUI() { if (!_target) return; - _target.SetMouseRay(MouseRay); CheckForInput(); + _target.CurrentComponent.DrawGUI(); switch (_target.actionTypeOnClick) { case RagdollFactory.ActionTypeOnClick.Create: - if(_target.IsFirstBoneSelected) - Handles.Label(_target.selectedBoneA.position + Vector3.up * 0.1f, "Select a second bone...", GUI_ALERT_STYLE); - switch (_target.componentType) - { - case RagdollFactory.ComponentType.Box: - DrawSelectableBones(); - break; - case RagdollFactory.ComponentType.Capsule: - DrawSelectableBones(); - break; - case RagdollFactory.ComponentType.ConfigurableJoint: - DrawSelectableBones(); - DrawJointHierarchy(); - DrawSelectableJoints(); - break; - case RagdollFactory.ComponentType.Rigidbody: - DrawSelectableBones(); - DrawSelectableRigidbodies(); - break; - } - break; - case RagdollFactory.ActionTypeOnClick.Select: - DrawSelectableElements(); - break; - case RagdollFactory.ActionTypeOnClick.Delete: - DrawSelectableElements(); + DrawSelectableBones(); break; } - - Selection.activeObject = _target.transform.gameObject; + SceneView.RepaintAll(); } - - private void DrawSelectableElements() - { - switch (_target.componentType) - { - case RagdollFactory.ComponentType.Box: - DrawSelectableColliders(); - break; - case RagdollFactory.ComponentType.Capsule: - DrawSelectableColliders(); - break; - case RagdollFactory.ComponentType.ConfigurableJoint: - DrawSelectableJoints(); - DrawJointHierarchy(); - break; - case RagdollFactory.ComponentType.Rigidbody: - DrawSelectableRigidbodies(); - break; - } - } private void CheckForInput() { Event e = Event.current; KeyCode keyCode = e.keyCode; - if (e.type == EventType.MouseDown && e.button == 0) - _target.MouseDown(); + // if (e.type == EventType.MouseDown && e.button == 0) + // _target.MouseDown(); if (e.type == EventType.KeyDown && keyCode == KeyCode.Escape) _target.DeselectBones(); } - private void DrawSelectableRigidbodies() - { - foreach (Rigidbody rb in _target.Rigidbodies) - { - Transform rbTransform = rb.transform; - Vector3 position = rbTransform.position; - - bool isHighlighted = _target.IsCursorLookingAt(rbTransform); - bool isSelected = _target.IsRigidbodySelected(rb); - - string kinematicLabel = rb.isKinematic ? "KINEMATIC" : "DYNAMIC"; - Color kinematicColor = Color.cyan * _target.normalColor; - - Handles.color = isHighlighted ? _target.selectedColor : _target.normalColor; - Handles.color = rb.isKinematic ? kinematicColor : Handles.color; - Handles.color = isSelected ? _target.selectedColor : Handles.color; - - Handles.DrawSolidDisc(position, SceneView.currentDrawingSceneView.camera.transform.forward, - _target.discRadius * rb.mass); - } - } - - private void DrawSelectableJoints() + private void DrawSelectableBones() { - foreach (ConfigurableJoint joint in _target.Joints) + foreach (Transform bone in _target.Bones) { - Transform jointTransform = joint.transform; - Vector3 position = jointTransform.position; + Handles.color = _target.normalColor; - bool isHighlighted = _target.IsCursorLookingAt(jointTransform); - bool isSelected = _target.IsJointSelected(joint); - - Handles.color = isHighlighted ? _target.selectedColor : _target.normalColor; - if (isSelected) + if (_target.selectedBoneA == bone) { - Handles.DrawSolidDisc(position, SceneView.currentDrawingSceneView.camera.transform.forward, - _target.discRadius); - Handles.Label(position, "JOINT " + joint.name); + Handles.color = _target.selectedColor; + Handles.Label(bone.position, "Waiting for second bone...", GUI_ALERT_STYLE); } - else - Handles.DrawWireDisc(position, SceneView.currentDrawingSceneView.camera.transform.forward, - _target.discRadius); - } - } - - private void DrawSelectableColliders() - { - foreach (Collider collider in _target.Colliders) - { - Transform colliderTransform = collider.transform; - Vector3 position = colliderTransform.position; - - bool isHighlighted = _target.IsCursorLookingAt(colliderTransform); - bool isSelected = _target.IsColliderSelected(collider); - - Handles.color = isHighlighted ? _target.selectedColor : _target.normalColor; - if (isSelected) + + bool bonePressed = Handles.Button( + bone.position, + SceneView.currentDrawingSceneView.camera.transform.rotation, + _target.discRadius, + _target.discRadius*2, + Handles.CircleHandleCap + ); + + if (bonePressed) { - Handles.DrawSolidDisc(position, SceneView.currentDrawingSceneView.camera.transform.forward, - _target.discRadius); - Handles.Label(position, collider.name); + if (!_target.IsFirstBoneSelected && !(_target.CurrentComponent is RigidbodyComponentState)) + { + _target.selectedBoneA = bone; + } + else + { + _target.selectedBoneB = bone; + _target.CurrentComponent.Create(); + _target.DeselectBones(); + } } - else - Handles.DrawWireDisc(position, SceneView.currentDrawingSceneView.camera.transform.forward, - _target.discRadius); - } - } - - private void DrawBoneHierarchyRecursive(Transform bone) - { - if (bone.childCount == 0) return; - Handles.color = _target.normalColor; - for (int i = 0; i < bone.childCount; i++) - { - Transform childBone = bone.GetChild(i); - Handles.DrawDottedLine(bone.position, childBone.position, 5f); - // Handles.DrawWireDisc(bone.position, SceneView.currentDrawingSceneView.camera.transform.forward, _target.discRadius); - DrawBoneHierarchyRecursive(childBone); - } - } - - private void DrawJointHierarchy() - { - if (_target.Joints.Length == 0) return; - - - foreach (ConfigurableJoint joint in _target.Joints) - { - bool hasConnectedBody = joint.connectedBody; - Transform jointTransform = joint.transform; - - Handles.color = hasConnectedBody ? Color.yellow : Color.red; - Handles.DrawWireDisc(jointTransform.position, - SceneView.currentDrawingSceneView.camera.transform.forward, _target.discRadius); - if (hasConnectedBody) Handles.DrawDottedLine(jointTransform.position, joint.connectedBody.position, 5f); - else Handles.Label(jointTransform.position, "MISSING CONNECTION", GUI_ALERT_STYLE); - VisualizeJointLimits(joint); - } - } - - private void VisualizeJointLimits(ConfigurableJoint joint, float radius = 0.1f, float opacity = 0.25f) - { - if (!joint) return; - - Vector3 normalXDirection = joint.transform.TransformDirection(joint.axis); - Vector3 fromXDirection = Vector3.Cross(joint.transform.TransformDirection(joint.axis), joint.transform.up); - - Vector3 normalYDirection = joint.transform.TransformDirection(Vector3.Cross(joint.axis, -Vector3.forward)); - Vector3 fromYDirection = Vector3.Cross(joint.transform.TransformDirection(joint.axis), normalYDirection); - - Vector3 normalZDirection = joint.transform.TransformDirection(Vector3.Cross(joint.axis, -Vector3.up)); - Vector3 fromZDirection = Vector3.Cross(joint.transform.TransformDirection(joint.axis), normalZDirection); - - // Angular X Limits - Handles.color = Color.red * new Color(1, 1, 1, 1); - Handles.DrawDottedLine(joint.transform.position, joint.transform.position + normalXDirection * radius / 2, - 5); - Handles.color = Color.red * new Color(1, 1, 1, opacity); - Handles.DrawSolidArc(joint.transform.position, normalXDirection, fromXDirection, - -joint.lowAngularXLimit.limit, radius); - Handles.DrawSolidArc(joint.transform.position, normalXDirection, fromXDirection, - -joint.highAngularXLimit.limit, radius); - - // Angular Y Limits - Handles.color = Color.green * new Color(1, 1, 1, 1); - Handles.DrawDottedLine(joint.transform.position, joint.transform.position + normalYDirection * radius / 2, - 5); - Handles.color = Color.green * new Color(1, 1, 1, opacity); - Handles.DrawSolidArc(joint.transform.position, normalYDirection, fromYDirection, joint.angularYLimit.limit, - radius); - Handles.DrawSolidArc(joint.transform.position, normalYDirection, fromYDirection, -joint.angularYLimit.limit, - radius); - - // Angular Z Limits - Handles.color = Color.blue * new Color(1, 1, 1, 1); - Handles.DrawDottedLine(joint.transform.position, joint.transform.position + normalZDirection * radius / 2, - 5); - Handles.color = Color.blue * new Color(1, 1, 1, opacity); - Handles.DrawSolidArc(joint.transform.position, normalZDirection, fromZDirection, joint.angularZLimit.limit, - radius); - Handles.DrawSolidArc(joint.transform.position, normalZDirection, fromZDirection, -joint.angularZLimit.limit, - radius); - } - - private void DrawSelectableBones() - { - foreach (Transform bone in _target.Bones) - { - bool isHighlighted = _target.IsCursorLookingAt(bone); - float radius = isHighlighted ? _target.capsuleColliderRadius : _target.discRadius; - Vector3 position = bone.position; - - Handles.color = isHighlighted ? _target.selectedColor : _target.normalColor; - Handles.DrawWireDisc(position, SceneView.currentDrawingSceneView.camera.transform.forward, radius); - if (isHighlighted) Handles.Label(position, bone.name); } } } diff --git a/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.cs b/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.cs index 55f2db6..b09fda0 100644 --- a/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.cs +++ b/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.cs @@ -124,7 +124,8 @@ private void DrawInspectorTabs() new(iconDelete, "Delete") }; - _target.componentType = (RagdollFactory.ComponentType)GUILayout.Toolbar((int)_target.componentType, componentTabs); + _target.componentType = (RagdollFactory.ComponentType)GUILayout.Toolbar((int)_target.componentType, componentTabs); + _target.SetState(_target.States[(int)_target.componentType]); _target.actionTypeOnClick = (RagdollFactory.ActionTypeOnClick)GUILayout.Toolbar((int)_target.actionTypeOnClick, actionTypeTabs); GUILayout.Space(15); @@ -158,7 +159,7 @@ private void DrawRigidbodyProperties() EditorGUILayout.PropertyField(_rigidbodyIsKinematic); if(GUILayout.Button("Delete Rigidbody")) - _target.DeleteSelectedRigidbody(); + _target.CurrentComponent.Delete(); //.DeleteSelectedRigidbody(); } private void DrawJointProperties() { @@ -169,7 +170,7 @@ private void DrawJointProperties() EditorGUILayout.PropertyField(_jointZAngle); if (GUILayout.Button("Delete Joint")) - _target.DeleteSelectedJoint(); + _target.CurrentComponent.Delete();//.DeleteSelectedJoint(); } private void DrawCapsuleColliderProperties() { @@ -178,9 +179,9 @@ private void DrawCapsuleColliderProperties() EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Convert to Box Collider")) - _target.ConvertSelectedColliderToBox(); - if (_target.LastSelectedCollider && GUILayout.Button("Delete")) - _target.DeleteSelectedCollider(); + _target.CurrentComponent.ConvertTo(new BoxCollider());//.ConvertSelectedColliderToBox(); + if (GUILayout.Button("Delete")) + _target.CurrentComponent.Delete();//.DeleteSelectedCollider(); EditorGUILayout.EndHorizontal(); } private void DrawBoxColliderProperties() @@ -191,9 +192,9 @@ private void DrawBoxColliderProperties() EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Convert to Capsule Collider")) - _target.ConvertSelectedColliderToCapsule(); + _target.CurrentComponent.ConvertTo(new CapsuleCollider());//.ConvertSelectedColliderToCapsule(); if (GUILayout.Button("Delete")) - _target.DeleteSelectedCollider(); + _target.CurrentComponent.Delete();//.DeleteSelectedCollider(); EditorGUILayout.EndHorizontal(); } #endregion diff --git a/Gann4Games/Ragdoll Factory/Scripts/RagdollFactory.cs b/Gann4Games/Ragdoll Factory/Scripts/RagdollFactory.cs index 7fca88c..d596c9c 100644 --- a/Gann4Games/Ragdoll Factory/Scripts/RagdollFactory.cs +++ b/Gann4Games/Ragdoll Factory/Scripts/RagdollFactory.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using UnityEngine; using System.Linq; +using Gann4Games.RagdollFactory.States; using UnityEditor; namespace Gann4Games.RagdollFactory @@ -9,15 +10,17 @@ namespace Gann4Games.RagdollFactory [ExecuteInEditMode] public partial class RagdollFactory : MonoBehaviour { - public Transform[] Bones => GetComponentsInChildren().Where(bone => bone.GetComponent() == false).ToArray(); + public RFComponentState CurrentComponent { get; private set; } - public Collider[] Colliders => GetComponentsInChildren(); - public Collider LastSelectedCollider { get; private set; } - public Rigidbody[] Rigidbodies => GetComponentsInChildren(); - public Rigidbody LastSelectedRigidbody { get; private set; } - public ConfigurableJoint[] Joints => GetComponentsInChildren(); - public ConfigurableJoint LastSelectedJoint { get; private set; } + public RFComponentState[] States { get; private set; } + + public BoxColliderComponentState BoxColliderComponentState; + public CapsuleColliderComponentState CapsuleColliderComponentState; + public ConfigurableJointComponentState ConfigurableJointComponentState; + public RigidbodyComponentState RigidbodyComponentState; + public Transform[] Bones => GetComponentsInChildren().Where(bone => bone.GetComponent() == false).ToArray(); + public Transform selectedBoneA; public Transform selectedBoneB; @@ -54,14 +57,6 @@ public partial class RagdollFactory : MonoBehaviour public Color normalColor = new Color(1, 1, 1, 0.25f); public Color selectedColor = new Color(1, 1, 0, 0.25f); - private Ray _mouseRay; - - - private CapsuleCollider _selectedCapsuleCollider; - private BoxCollider _selectedBoxCollider; - - private List _componentHistory = new List(); - public bool IsFirstBoneSelected => selectedBoneA != null; public enum ActionTypeOnClick @@ -74,605 +69,34 @@ public enum ComponentType Capsule, Box, ConfigurableJoint, Rigidbody } - // Cursor data is sent through the custom editor script to this function. - public void SetMouseRay(Ray ray) => _mouseRay = ray; - - - #region Closest elements to cursor - // Checkers for close components - Yes, there's a code smell here. - private Transform ClosestBoneToCursor() - { - Transform closestObject = Bones[0]; - float closestAccuarcy = CursorSelectionAccuracy(closestObject); - foreach (Transform currentBone in Bones) - { - float currentAccuarcy = CursorSelectionAccuracy(currentBone); - if (currentAccuarcy > closestAccuarcy) - { - closestObject = currentBone; - closestAccuarcy = currentAccuarcy; - } - } - return closestObject; - } - private Collider ClosestColliderToCursor() - { - if(Colliders.Length == 0) return null; - - Collider closestObject = Colliders[0]; - float closestAccuarcy = CursorSelectionAccuracy(closestObject.transform); - foreach (Collider currentCollider in Colliders) - { - float currentAccuarcy = CursorSelectionAccuracy(currentCollider.transform); - if(currentAccuarcy > closestAccuarcy) - { - closestObject = currentCollider; - closestAccuarcy = currentAccuarcy; - } - } - return closestObject; - } - private Rigidbody ClosestRigidbodyToCursor() - { - if (Rigidbodies.Length == 0) return null; - - Rigidbody closestObject = Rigidbodies[0]; - float closestAccuarcy = CursorSelectionAccuracy(closestObject.transform); - foreach (Rigidbody currentRigidbody in Rigidbodies) - { - float currentAccuarcy = CursorSelectionAccuracy(currentRigidbody.transform); - if (currentAccuarcy > closestAccuarcy) - { - closestObject = currentRigidbody; - closestAccuarcy = currentAccuarcy; - } - } - - return closestObject; - } - private ConfigurableJoint ClosestJointToCursor() - { - if (Joints.Length == 0) return null; - - ConfigurableJoint closestObject = Joints[0]; - float closestAccuarcy = CursorSelectionAccuracy(closestObject.transform); - foreach(ConfigurableJoint currentJoint in Joints) - { - float currentAccuarcy = CursorSelectionAccuracy(currentJoint.transform); - if(currentAccuarcy > closestAccuarcy) - { - closestObject = currentJoint; - closestAccuarcy = currentAccuarcy; - } - } - return closestObject; - } - #endregion - - #region Selected elements check - /// - /// Checks if the given collider is being currently selected. - /// - /// The collider to check - /// If it is selected or not - public bool IsColliderSelected(Collider col) => col == LastSelectedCollider; - - /// - /// Checks if the given rigidbody is being currently selected. - /// - /// The rigidbody to check - /// If it is selected or not - public bool IsRigidbodySelected(Rigidbody rb) => rb == LastSelectedRigidbody; - - /// - /// Checks if the given configurable joint is being currently selected. - /// - /// The joint to check - /// If it is selected or not - public bool IsJointSelected(ConfigurableJoint joint) => joint == LastSelectedJoint; - #endregion - - private float CursorSelectionAccuracy(Transform obj) - { - Vector3 objPosition = obj.position; - Vector3 dirTowardsObj = (objPosition - _mouseRay.origin).normalized; - return Vector3.Dot(dirTowardsObj, _mouseRay.direction); - } - - /// - /// Checks if the given object is one of the closest from all the supported types. - /// - /// - /// - public bool IsClosestOfAnyType(Transform obj) => obj == ClosestBoneToCursor() || obj == ClosestColliderToCursor() || obj == ClosestRigidbodyToCursor() || obj == ClosestJointToCursor(); - - public bool IsCursorLookingAt(Transform obj) => CursorSelectionAccuracy(obj) > 0.99f && IsClosestOfAnyType(obj); - - /// - /// Execute an action when left mouse click is pressed. - /// - public void MouseDown() - { - switch (actionTypeOnClick) - { - case ActionTypeOnClick.Create: - bool useFirstBone = componentType != ComponentType.Rigidbody && !IsFirstBoneSelected; - if(useFirstBone) SelectFirstBone(); - else - { - SelectSecondBone(); - CreateComponent(); - } - break; - case ActionTypeOnClick.Select: - SelectComponent(); - break; - case ActionTypeOnClick.Delete: - SelectComponent(); - DeleteSelectedComponent(); - break; - } - } - - private void DeleteSelectedComponent() - { - switch (componentType) - { - case ComponentType.Box: - DeleteSelectedCollider(); - break; - case ComponentType.Capsule: - DeleteSelectedCollider(); - break; - case ComponentType.ConfigurableJoint: - DeleteSelectedJoint(); - break; - case ComponentType.Rigidbody: - DeleteSelectedRigidbody(); - break; - } - } + public void SetState(RFComponentState state) => CurrentComponent = state; - private void SelectFirstBone() - { - selectedBoneA = ClosestBoneToCursor(); - } - - private void SelectSecondBone() - { - selectedBoneB = ClosestBoneToCursor(); - } - public void DeselectBones() { selectedBoneA = null; selectedBoneB = null; } - - private void SelectComponent() - { - switch (componentType) - { - case ComponentType.Box: - SelectCollider(); - break; - case ComponentType.Capsule: - SelectCollider(); - break; - case ComponentType.ConfigurableJoint: - SelectJoint(); - break; - case ComponentType.Rigidbody: - SelectRigidbody(); - break; - } - } - - private void SelectRigidbody() - { - Rigidbody closestRigidbody = ClosestRigidbodyToCursor(); - if(closestRigidbody == null) - return; - - LastSelectedRigidbody = closestRigidbody; - - // Obtain original values - rigidbodyMass = closestRigidbody.mass; - rigidbodyDrag = closestRigidbody.drag; - rigidbodyAngularDrag = closestRigidbody.angularDrag; - rigidbodyUseGravity = closestRigidbody.useGravity; - rigidbodyIsKinematic = closestRigidbody.isKinematic; - } - - private void SelectJoint() - { - ConfigurableJoint closestJoint = ClosestJointToCursor(); - if(closestJoint == null) - return; - - LastSelectedJoint = closestJoint; - LastSelectedRigidbody = closestJoint.GetComponent(); - - // Obtain original values - jointAxis = closestJoint.axis; - jointLowXLimit = -closestJoint.lowAngularXLimit.limit; - jointHighXLimit = closestJoint.highAngularXLimit.limit; - jointYLimit = closestJoint.angularYLimit.limit; - jointZLimit = closestJoint.angularZLimit.limit; - - componentType = ComponentType.ConfigurableJoint; - } - - private void SelectCollider() - { - Collider closestCollider = ClosestColliderToCursor(); - if (closestCollider == null) - return; - - LastSelectedCollider = closestCollider; - - // Check collider types - if (closestCollider is CapsuleCollider) - { - _selectedCapsuleCollider = closestCollider as CapsuleCollider; - capsuleColliderRadius = _selectedCapsuleCollider.radius; - capsuleColliderLength = _selectedCapsuleCollider.center.z * 2; - componentType = ComponentType.Capsule; - } - else if (closestCollider is BoxCollider) - { - _selectedBoxCollider = closestCollider as BoxCollider; - boxColliderWidth = _selectedBoxCollider.size.x; - boxColliderDepth = _selectedBoxCollider.size.y; - boxColliderLength = _selectedBoxCollider.center.z * 2; - componentType = ComponentType.Box; - } - else - { - throw new Exception("Collider type not supported!"); - } - } - - /// - /// Creates the component based on the two selected bones, the bones will be deselected afterwards. - /// - private void CreateComponent() - { - switch (componentType) - { - case ComponentType.Capsule: - CreateCapsuleCollider(selectedBoneA, selectedBoneB); - break; - case ComponentType.Box: - CreateBoxCollider(selectedBoneA, selectedBoneB); - break; - case ComponentType.ConfigurableJoint: - CreateOrSelectConfigurableJoint(selectedBoneA, selectedBoneB); - break; - case ComponentType.Rigidbody: - CreateRigidbody(selectedBoneB); - break; - } - DeselectBones(); - } - - - - #region Component creation - private T GetOrAddComponent(GameObject target) where T : Component - { - if(target.TryGetComponent(out T component)) - return component; - - T comp = target.AddComponent(); - Undo.RegisterCreatedObjectUndo(comp, "Created component " + comp.name); - return comp; - } - - private void CreateRigidbody(Transform obj) - { - Rigidbody rb = GetOrAddComponent(obj.gameObject); - LastSelectedRigidbody = rb; - LastSelectedRigidbody.mass = rigidbodyMass; - LastSelectedRigidbody.drag = rigidbodyDrag; - LastSelectedRigidbody.angularDrag = rigidbodyAngularDrag; - LastSelectedRigidbody.useGravity = rigidbodyUseGravity; - LastSelectedRigidbody.isKinematic = rigidbodyIsKinematic; - - _componentHistory.Add(rb); - } - - private void CreateOrSelectConfigurableJoint(Transform objA, Transform objB) - { - if (!objB.IsChildOf(objA)) - throw new Exception("The second bone must be child of the first bone!"); - - Rigidbody rigidbodyA = GetOrAddComponent(objA.gameObject); - Rigidbody rigidbodyB = GetOrAddComponent(objB.gameObject); - ConfigurableJoint joint = GetOrAddComponent(objB.gameObject); - - LastSelectedJoint = joint; - LastSelectedRigidbody = rigidbodyB; - - LastSelectedJoint.connectedBody = rigidbodyA; - LastSelectedJoint.xMotion = ConfigurableJointMotion.Locked; - LastSelectedJoint.yMotion = ConfigurableJointMotion.Locked; - LastSelectedJoint.zMotion = ConfigurableJointMotion.Locked; - LastSelectedJoint.angularXMotion = ConfigurableJointMotion.Limited; - LastSelectedJoint.angularYMotion = ConfigurableJointMotion.Limited; - LastSelectedJoint.angularZMotion = ConfigurableJointMotion.Limited; - - LastSelectedJoint.axis = jointAxis; - LastSelectedJoint.lowAngularXLimit = new SoftJointLimit() {limit = -jointLowXLimit}; - LastSelectedJoint.highAngularXLimit = new SoftJointLimit() {limit = jointHighXLimit}; - LastSelectedJoint.angularYLimit = new SoftJointLimit() {limit = jointYLimit}; - LastSelectedJoint.angularZLimit = new SoftJointLimit() {limit = jointZLimit}; - - _componentHistory.Add(joint); - _componentHistory.Add(rigidbodyB); - _componentHistory.Add(rigidbodyA); - } - - private void CreateCapsuleCollider(Transform objA, Transform objB) - { - if (!objB.IsChildOf(objA)) - throw new Exception("The second bone must be child of the first bone!"); - - float distance = Vector3.Distance(objA.position, objB.position); - - GameObject collisionObject = new GameObject(objA.name + " - " + objB.name); - collisionObject.transform.SetParent(objA); - collisionObject.transform.localPosition = Vector3.zero; - collisionObject.transform.forward = objB.position - objA.position; - collisionObject.transform.localScale = Vector3.one; - _selectedCapsuleCollider = collisionObject.AddComponent(); - _selectedCapsuleCollider.direction = 2; - _selectedCapsuleCollider.radius = capsuleColliderRadius; - _selectedCapsuleCollider.center = Vector3.forward * distance / 2; - _selectedCapsuleCollider.height = distance + _selectedCapsuleCollider.radius; - - capsuleColliderLength = distance; - - Undo.RegisterCreatedObjectUndo(collisionObject, "Created Capsule Collider Object"); - Undo.RegisterCompleteObjectUndo(this, "Created Capsule Collider Object"); - - LastSelectedCollider = _selectedCapsuleCollider; - _componentHistory.Add(_selectedCapsuleCollider); - } - - private void CreateBoxCollider(Transform objA, Transform objB) - { - if (!objB.IsChildOf(objA)) - throw new Exception("The second bone must be child of the first bone!"); - - float distance = Vector3.Distance(objA.position, objB.position); - float height = distance / 2; - GameObject collisionObject = new GameObject(objA.name + " - " + objB.name); - collisionObject.transform.SetParent(objA); - collisionObject.transform.localPosition = Vector3.zero; - collisionObject.transform.forward = objB.position - objA.position; - collisionObject.transform.localScale = Vector3.one; - _selectedBoxCollider = collisionObject.AddComponent(); - _selectedBoxCollider.center = Vector3.forward * height; - _selectedBoxCollider.size = new Vector3(boxColliderWidth, boxColliderDepth, distance); - - boxColliderLength = height; - - Undo.RegisterCreatedObjectUndo(collisionObject, "Created Box Collider Object"); - Undo.RegisterCompleteObjectUndo(this, "Created Box Collider Object"); - - - LastSelectedCollider = _selectedBoxCollider; - _componentHistory.Add(_selectedBoxCollider); - } - #endregion - - /// - /// Converts a box collider to a capsule collider - /// - public void ConvertSelectedColliderToCapsule() - { - BoxCollider col = _selectedBoxCollider; - GameObject collisionObject = col.gameObject; - - float distance = col.size.z; - float radius = Mathf.Max(col.size.x, col.size.y) / 2; - float height = col.size.z / 2; - - capsuleColliderLength = distance; - capsuleColliderRadius = radius; - - Undo.DestroyObjectImmediate(col); - - CapsuleCollider capsuleCollider = Undo.AddComponent(collisionObject); - capsuleCollider.direction = 2; - capsuleCollider.radius = radius; - capsuleCollider.center = Vector3.forward * height; - capsuleCollider.height = distance + capsuleCollider.radius; - _selectedCapsuleCollider = capsuleCollider; - - _componentHistory[_componentHistory.Count - 1] = capsuleCollider; - - Undo.RegisterCompleteObjectUndo(collisionObject, "Converted Box Collider to Capsule Collider"); - - componentType = ComponentType.Capsule; - - LastSelectedCollider = capsuleCollider; - } - - /// - /// Converts a capsule collider into a box collider - /// - public void ConvertSelectedColliderToBox() - { - CapsuleCollider capsuleCollider = _selectedCapsuleCollider; - GameObject collisionObject = capsuleCollider.gameObject; - - float radius = capsuleCollider.radius * 2; - float distance = capsuleCollider.center.z * 2; - float height = distance / 2; - - boxColliderDepth = radius; - boxColliderWidth = radius; - boxColliderLength = distance; - - Undo.DestroyObjectImmediate(capsuleCollider); - - BoxCollider boxCollider = Undo.AddComponent(collisionObject); - boxCollider.center = Vector3.forward * height; - boxCollider.size = new Vector3(radius, radius, distance); - _selectedBoxCollider = boxCollider; - - _componentHistory[_componentHistory.Count - 1] = boxCollider; - - Undo.RegisterCompleteObjectUndo(collisionObject, "Converted Capsule Collider to Box Collider"); - - componentType = ComponentType.Box; - LastSelectedCollider = boxCollider; - } - - public void TryDeleteObject(Component component) - { - try - { - DeleteObject(component); - } - catch (InvalidOperationException e) - { - Debug.LogWarning("UNABLE TO DELETE GAMEOBJECT! Removing component instead.\nMore information:\n" + e); - DeleteComponent(component); - } - } - - public void DeleteSelectedRigidbody() - { - DeleteComponent(LastSelectedRigidbody); - } - - public void DeleteSelectedJoint() - { - ConfigurableJoint nextSelection = LastSelectedJoint.connectedBody?.GetComponent(); - DeleteComponent(LastSelectedJoint); - LastSelectedJoint = nextSelection; - DeleteSelectedRigidbody(); - } - - public void DeleteSelectedCollider() - { - TryDeleteObject(LastSelectedCollider); - } - - public void DeleteLastComponent() - { - Component component = _componentHistory[_componentHistory.Count - 1]; - DeleteComponent(component); - } - - public void DeleteComponent(Component component) - { - _componentHistory.Remove(component); - Undo.DestroyObjectImmediate(component); - Undo.RegisterCompleteObjectUndo(this, "Deleted collider component"); - } - - public void DeleteObject(Component component) - { - _componentHistory.Remove(component); - Undo.DestroyObjectImmediate(component.gameObject); - Undo.RegisterCompleteObjectUndo(this, "Deleted object"); - } - - public void DeleteAllColliders() - { - // Using an int variable because the length of the list changes with every deleted element. - // Therefore it creates bugs if you delete all colliders at once because the loop will stop before deleting all colliders. - int timesToRepeat = _componentHistory.Count; //_colliderHistory.Count; - for (int i = 0; i < timesToRepeat; i++) - { - Component component = _componentHistory[i]; - - if (!(component is Collider)) continue; - - TryDeleteObject(component); - i--; - } - } private void OnValidate() { - ValidateCapsuleColliderValues(); - ValidateBoxColliderValues(); - ValidateJointValues(); - ValidateRigidbodyValues(); - } - - private void ValidateRigidbodyValues() - { - if (!LastSelectedRigidbody) return; - - LastSelectedRigidbody.mass = rigidbodyMass; - LastSelectedRigidbody.drag = rigidbodyDrag; - LastSelectedRigidbody.angularDrag = rigidbodyAngularDrag; - LastSelectedRigidbody.useGravity = rigidbodyUseGravity; - LastSelectedRigidbody.isKinematic = rigidbodyIsKinematic; - } - - private void ValidateJointValues() - { - if (!LastSelectedJoint) return; - - jointAxis.Normalize(); - - var lowAngularXLimit = LastSelectedJoint.lowAngularXLimit; - var highAngularXLimit = LastSelectedJoint.highAngularXLimit; - var angularYLimit = LastSelectedJoint.angularYLimit; - var angularZLimit = LastSelectedJoint.angularZLimit; - - lowAngularXLimit.limit = -jointLowXLimit; - highAngularXLimit.limit = jointHighXLimit; - angularYLimit.limit = jointYLimit; - angularZLimit.limit = jointZLimit; - - LastSelectedJoint.lowAngularXLimit = lowAngularXLimit; - LastSelectedJoint.highAngularXLimit = highAngularXLimit; - LastSelectedJoint.angularYLimit = angularYLimit; - LastSelectedJoint.angularZLimit = angularZLimit; - LastSelectedJoint.axis = jointAxis; - } - - private void ValidateCapsuleColliderValues() - { - if (!_selectedCapsuleCollider) return; - - // Limit values to 0 or higher - if(capsuleColliderRadius < 0) capsuleColliderRadius = 0; - if (capsuleColliderLength < 0) capsuleColliderLength = 0; - - _selectedCapsuleCollider.radius = capsuleColliderRadius; - - // Adjust capsule size and position to fit bones - float distance = Vector3.Distance(_selectedCapsuleCollider.transform.position, _selectedCapsuleCollider.transform.position + _selectedCapsuleCollider.transform.forward * capsuleColliderLength); - _selectedCapsuleCollider.center = Vector3.forward * distance / 2; - _selectedCapsuleCollider.height = distance + _selectedCapsuleCollider.radius; - } - - private void ValidateBoxColliderValues() - { - if (!_selectedBoxCollider) return; - - // Limit values to 0 or higher - if(boxColliderDepth < 0) boxColliderDepth = 0; - if(boxColliderWidth < 0) boxColliderWidth = 0; - - // Adjust box size and position to fit bones - _selectedBoxCollider.center = Vector3.forward * boxColliderLength / 2; - _selectedBoxCollider.size = new Vector3(boxColliderWidth, boxColliderDepth, boxColliderLength); + CurrentComponent.Update(); } private void OnEnable() - { - if(_componentHistory.Count == 0 && Colliders.Length > 0) - _componentHistory.AddRange(Colliders); + { + CapsuleColliderComponentState = new CapsuleColliderComponentState(this); + BoxColliderComponentState = new BoxColliderComponentState(this); + ConfigurableJointComponentState = new ConfigurableJointComponentState(this); + RigidbodyComponentState = new RigidbodyComponentState(this); + States = new RFComponentState[] + { + CapsuleColliderComponentState, + BoxColliderComponentState, + ConfigurableJointComponentState, + RigidbodyComponentState + }; + + CurrentComponent = CapsuleColliderComponentState; } } } diff --git a/Gann4Games/Ragdoll Factory/Scripts/States.meta b/Gann4Games/Ragdoll Factory/Scripts/States.meta new file mode 100644 index 0000000..6ba5fea --- /dev/null +++ b/Gann4Games/Ragdoll Factory/Scripts/States.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 89e10be1d310433e880d41fa12815ac1 +timeCreated: 1689048655 \ No newline at end of file diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/BoxColliderComponentState.cs b/Gann4Games/Ragdoll Factory/Scripts/States/BoxColliderComponentState.cs new file mode 100644 index 0000000..1f2eb3a --- /dev/null +++ b/Gann4Games/Ragdoll Factory/Scripts/States/BoxColliderComponentState.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace Gann4Games.RagdollFactory.States +{ + public class BoxColliderComponentState : RFComponentState + { + private bool Pressed(BoxCollider col) + { + return Handles.Button( + col.transform.position, + col.transform.rotation, + Mathf.Max(col.size.x, col.size.y), + 0.1f, + Handles.CubeHandleCap + ); + } + public BoxColliderComponentState(RagdollFactory context) : base(context) + { + ComponentList = new List(); + ComponentList.AddRange(Context.GetComponentsInChildren()); + } + + public override void Create() + { + Transform objA = Context.selectedBoneA, objB = Context.selectedBoneB; + + float distance = Vector3.Distance(objA.position, objB.position); + float height = distance / 2; + GameObject collisionObject = new GameObject(objA.name + " - " + objB.name); + collisionObject.transform.SetParent(objA); + collisionObject.transform.localPosition = Vector3.zero; + collisionObject.transform.forward = objB.position - objA.position; + collisionObject.transform.localScale = Vector3.one; + BoxCollider _selectedBoxCollider = Undo.AddComponent(collisionObject); + _selectedBoxCollider.center = Vector3.forward * height; + _selectedBoxCollider.size = new Vector3(Context.boxColliderWidth, Context.boxColliderDepth, distance); + + Context.boxColliderLength = height; + + Undo.RegisterCreatedObjectUndo(collisionObject, "Created Box Collider Object"); + Undo.RegisterCompleteObjectUndo(Context, "Created Box Collider Object"); + + + Select(_selectedBoxCollider); + } + + public override void ConvertTo(Component component) + { + if (component is BoxCollider) + { + BoxCollider col = SelectedComponent as BoxCollider; + GameObject collisionObject = col.gameObject; + + ComponentList.Remove(col); + + float distance = col.size.z; + float radius = Mathf.Max(col.size.x, col.size.y) / 2; + float height = col.size.z / 2; + + Context.capsuleColliderLength = distance; + Context.capsuleColliderRadius = radius; + + Undo.DestroyObjectImmediate(col); + + CapsuleCollider capsuleCollider = Undo.AddComponent(collisionObject); + capsuleCollider.direction = 2; + capsuleCollider.radius = radius; + capsuleCollider.center = Vector3.forward * height; + capsuleCollider.height = distance + capsuleCollider.radius; + + Undo.RegisterCompleteObjectUndo(collisionObject, "Converted Box Collider to Capsule Collider"); + + Context.CapsuleColliderComponentState.Select(capsuleCollider); + } + } + + public override void DrawGUI() + { + foreach(BoxCollider collider in ComponentList.ToArray()) + { + if (!collider) continue; + Handles.color = IsSelected(collider) ? Context.selectedColor : Context.normalColor; + + if(IsSelected(collider)) + collider.transform.position = Handles.PositionHandle(collider.transform.position, collider.transform.rotation); + + switch (Context.actionTypeOnClick) + { + case RagdollFactory.ActionTypeOnClick.Select: + if (Pressed(collider)) + Select(collider); + break; + case RagdollFactory.ActionTypeOnClick.Delete: + if (Pressed(collider)) + { + Select(collider); + Delete(); + } + break; + } + } + } + + public override void Update() + { + if (!SelectedComponent) return; + + BoxCollider _selectedBoxCollider = SelectedComponent as BoxCollider; + + // Limit values to 0 or higher + if(Context.boxColliderDepth < 0) Context.boxColliderDepth = 0; + if(Context.boxColliderWidth < 0) Context.boxColliderWidth = 0; + + // Adjust box size and position to fit bones + _selectedBoxCollider.center = Vector3.forward * Context.boxColliderLength / 2; + _selectedBoxCollider.size = new Vector3(Context.boxColliderWidth, Context.boxColliderDepth, Context.boxColliderLength); + } + + public override void Select(Component component) + { + base.Select(component); + Context.boxColliderDepth = (SelectedComponent as BoxCollider).size.y; + Context.boxColliderWidth = (SelectedComponent as BoxCollider).size.x; + Context.boxColliderLength = (SelectedComponent as BoxCollider).size.z; + } + + public override void Delete() => DeleteSelectedGameObject(); + } +} \ No newline at end of file diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/BoxColliderComponentState.cs.meta b/Gann4Games/Ragdoll Factory/Scripts/States/BoxColliderComponentState.cs.meta new file mode 100644 index 0000000..b2c95cb --- /dev/null +++ b/Gann4Games/Ragdoll Factory/Scripts/States/BoxColliderComponentState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f5217cbe0129470c8f434c8d7c5b3634 +timeCreated: 1689048904 \ No newline at end of file diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/CapsuleColliderComponentState.cs b/Gann4Games/Ragdoll Factory/Scripts/States/CapsuleColliderComponentState.cs new file mode 100644 index 0000000..cb33bf5 --- /dev/null +++ b/Gann4Games/Ragdoll Factory/Scripts/States/CapsuleColliderComponentState.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace Gann4Games.RagdollFactory.States +{ + public class CapsuleColliderComponentState : RFComponentState + { + private bool Pressed(CapsuleCollider col) + { + return Handles.Button( + col.transform.position, + Quaternion.identity, + col.radius, + 0.1f, + Handles.SphereHandleCap + ); + } + + public CapsuleColliderComponentState(RagdollFactory context) : base(context) + { + ComponentList = new List(); + ComponentList.AddRange(Context.GetComponentsInChildren()); + } + + public override void Create() + { + Transform objA = Context.selectedBoneA, objB = Context.selectedBoneB; + // if (!objB.IsChildOf(objA)) + // throw new Exception("The second bone must be child of the first bone!"); + + float distance = Vector3.Distance(objA.position, objB.position); + + GameObject collisionObject = new GameObject(objA.name + " - " + objB.name); + collisionObject.transform.SetParent(objA); + collisionObject.transform.localPosition = Vector3.zero; + collisionObject.transform.forward = objB.position - objA.position; + collisionObject.transform.localScale = Vector3.one; + CapsuleCollider selectedCapsuleCollider = Undo.AddComponent(collisionObject); + selectedCapsuleCollider.direction = 2; + selectedCapsuleCollider.radius = Context.capsuleColliderRadius; + selectedCapsuleCollider.center = Vector3.forward * distance / 2; + selectedCapsuleCollider.height = distance + selectedCapsuleCollider.radius; + + Context.capsuleColliderLength = distance; + + Undo.RegisterCompleteObjectUndo(Context, "Created Capsule Collider Object"); + + Select(selectedCapsuleCollider); + } + + public override void ConvertTo(Component component) + { + if (component is BoxCollider) + { + CapsuleCollider capsuleCollider = SelectedComponent as CapsuleCollider; + GameObject collisionObject = capsuleCollider.gameObject; + + ComponentList.Remove(capsuleCollider); + + float radius = capsuleCollider.radius * 2; + float distance = capsuleCollider.center.z * 2; + float height = distance / 2; + + Context.boxColliderDepth = radius; + Context.boxColliderWidth = radius; + Context.boxColliderLength = distance; + + Undo.DestroyObjectImmediate(capsuleCollider); + + BoxCollider boxCollider = Undo.AddComponent(collisionObject); + boxCollider.center = Vector3.forward * height; + boxCollider.size = new Vector3(radius, radius, distance); + + Context.BoxColliderComponentState.Select(boxCollider); + + Undo.RegisterCompleteObjectUndo(collisionObject, "Converted Capsule Collider to Box Collider"); + + Context.SetState(Context.BoxColliderComponentState); + } + } + + public override void DrawGUI() + { + foreach(CapsuleCollider collider in ComponentList.ToArray()) + { + if (!collider) continue; + Handles.color = IsSelected(collider) ? Context.selectedColor : Context.normalColor; + + if(IsSelected(collider)) + collider.transform.position = Handles.PositionHandle(collider.transform.position, collider.transform.rotation); + + switch (Context.actionTypeOnClick) + { + case RagdollFactory.ActionTypeOnClick.Select: + if (Pressed(collider)) + Select(collider); + break; + case RagdollFactory.ActionTypeOnClick.Delete: + if (Pressed(collider)) + { + Select(collider); + Delete(); + } + break; + } + } + } + + public override void Update() + { + if (!SelectedComponent) return; + + CapsuleCollider selectedCapsuleCollider = SelectedComponent as CapsuleCollider; + + selectedCapsuleCollider.radius = Context.capsuleColliderRadius; + + // Adjust capsule size and position to fit bones + // float distance = Vector3.Distance(selectedCapsuleCollider.transform.position, selectedCapsuleCollider.transform.position + selectedCapsuleCollider.transform.forward * ); + selectedCapsuleCollider.center = Vector3.forward * Context.capsuleColliderLength / 2; + selectedCapsuleCollider.height = Context.capsuleColliderLength + selectedCapsuleCollider.radius; + } + + public override void Select(Component component) + { + base.Select(component); + Context.capsuleColliderRadius = (SelectedComponent as CapsuleCollider).radius; + Context.capsuleColliderLength = (SelectedComponent as CapsuleCollider).height - Context.capsuleColliderRadius; + } + + public override void Delete() => DeleteSelectedGameObject(); + } +} \ No newline at end of file diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/CapsuleColliderComponentState.cs.meta b/Gann4Games/Ragdoll Factory/Scripts/States/CapsuleColliderComponentState.cs.meta new file mode 100644 index 0000000..3fe801e --- /dev/null +++ b/Gann4Games/Ragdoll Factory/Scripts/States/CapsuleColliderComponentState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 969d5e05f2b24761a5c20fba3d0e541d +timeCreated: 1689048893 \ No newline at end of file diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/ConfigurableJointComponentState.cs b/Gann4Games/Ragdoll Factory/Scripts/States/ConfigurableJointComponentState.cs new file mode 100644 index 0000000..4fa30df --- /dev/null +++ b/Gann4Games/Ragdoll Factory/Scripts/States/ConfigurableJointComponentState.cs @@ -0,0 +1,275 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEditor.IMGUI.Controls; +using UnityEngine; + +namespace Gann4Games.RagdollFactory.States +{ + public class ConfigurableJointComponentState : RFComponentState + { + private bool Pressed(ConfigurableJoint col) + { + Handles.color = Context.selectedColor; + return Handles.Button( + col.transform.position, + col.transform.rotation, + 0.05f, + 0.05f, + Handles.CylinderHandleCap + ); + } + private GUIStyle GUI_ALERT_STYLE = new GUIStyle(); + private ArcHandle arcHandleXLow = new ArcHandle(); + private ArcHandle arcHandleXHigh = new ArcHandle(); + private ArcHandle arcHandleY = new ArcHandle(); + private ArcHandle arcHandleZ = new ArcHandle(); + public ConfigurableJointComponentState(RagdollFactory context) : base(context) + { + ComponentList = new List(); + ComponentList.AddRange(Context.GetComponentsInChildren()); + + GUI_ALERT_STYLE.fontStyle = FontStyle.Bold; + GUI_ALERT_STYLE.normal.textColor = Color.red; + + arcHandleXLow.SetColorWithRadiusHandle(Color.red, 0.2f); + arcHandleXHigh.SetColorWithRadiusHandle(Color.red, 0.2f); + arcHandleY.SetColorWithRadiusHandle(Color.green, 0.2f); + arcHandleZ.SetColorWithRadiusHandle(Color.blue, 0.2f); + } + + public override void Create() + { + Transform objA = Context.selectedBoneA, objB = Context.selectedBoneB; + + Rigidbody rigidbodyA = GetOrAddComponent(objA.gameObject); + Rigidbody rigidbodyB = GetOrAddComponent(objB.gameObject); + ConfigurableJoint joint = GetOrAddComponent(objB.gameObject); + + + ConfigurableJoint lastSelectedJoint = joint; + Rigidbody lastSelectedRigidbody = rigidbodyB; + + lastSelectedJoint.connectedBody = rigidbodyA; + lastSelectedJoint.xMotion = ConfigurableJointMotion.Locked; + lastSelectedJoint.yMotion = ConfigurableJointMotion.Locked; + lastSelectedJoint.zMotion = ConfigurableJointMotion.Locked; + lastSelectedJoint.angularXMotion = ConfigurableJointMotion.Limited; + lastSelectedJoint.angularYMotion = ConfigurableJointMotion.Limited; + lastSelectedJoint.angularZMotion = ConfigurableJointMotion.Limited; + + lastSelectedJoint.axis = Context.jointAxis; + lastSelectedJoint.lowAngularXLimit = new SoftJointLimit() {limit = -Context.jointLowXLimit}; + lastSelectedJoint.highAngularXLimit = new SoftJointLimit() {limit = Context.jointHighXLimit}; + lastSelectedJoint.angularYLimit = new SoftJointLimit() {limit = Context.jointYLimit}; + lastSelectedJoint.angularZLimit = new SoftJointLimit() {limit = Context.jointZLimit}; + + Context.RigidbodyComponentState.Select(rigidbodyA); + Context.RigidbodyComponentState.Select(rigidbodyB); + Select(joint); + } + + public override void ConvertTo(Component component) + { + throw new System.NotImplementedException(); + } + + public override void DrawGUI() + { + foreach (ConfigurableJoint joint in ComponentList.ToArray()) + { + if(IsSelected(joint)) DisplayJointHandles(joint); + else VisualizeJointLimits(joint); + + switch (Context.actionTypeOnClick) + { + case RagdollFactory.ActionTypeOnClick.Create: + DisplayJointStatus(joint); + break; + case RagdollFactory.ActionTypeOnClick.Select: + if (Pressed(joint)) + Select(joint); + break; + case RagdollFactory.ActionTypeOnClick.Delete: + DisplayJointStatus(joint); + if (Pressed(joint)) + { + Select(joint); + Delete(); + } + break; + } + } + } + + private void DisplayJointStatus(ConfigurableJoint joint) + { + bool hasConnectedBody = joint.connectedBody; + Transform jointTransform = joint.transform; + + Handles.color = hasConnectedBody ? Color.yellow : Color.red; + Handles.DrawWireDisc(jointTransform.position, + SceneView.currentDrawingSceneView.camera.transform.forward, Context.discRadius); + if (hasConnectedBody) Handles.DrawDottedLine(jointTransform.position, joint.connectedBody.position, 5f); + else Handles.Label(jointTransform.position, "MISSING CONNECTION", GUI_ALERT_STYLE); + } + + private void DisplayJointHandles(ConfigurableJoint joint) + { + if (!joint) return; + + Vector3 normalXDirection = joint.transform.TransformDirection(joint.axis); + Vector3 fromXDirection = Vector3.Cross(joint.transform.TransformDirection(joint.axis), joint.transform.up); + + Vector3 normalYDirection = joint.transform.TransformDirection(Vector3.Cross(joint.axis, -Vector3.forward)); + Vector3 fromYDirection = Vector3.Cross(joint.transform.TransformDirection(joint.axis), normalYDirection); + + Vector3 normalZDirection = joint.transform.TransformDirection(Vector3.Cross(joint.axis, -Vector3.up)); + Vector3 fromZDirection = Vector3.Cross(joint.transform.TransformDirection(joint.axis), normalZDirection); + + // Load arc values + arcHandleXLow.angle = Context.jointLowXLimit; + arcHandleXHigh.angle = -Context.jointHighXLimit; + arcHandleY.angle = Context.jointYLimit; + arcHandleZ.angle = Context.jointZLimit; + + arcHandleXLow.radius = 0.1f; + arcHandleXHigh.radius = 0.1f; + arcHandleY.radius = 0.1f; + arcHandleZ.radius = 0.1f; + + // Draw opposite handles for preview + Handles.color = Color.green * new Color(1, 1, 1, 0.2f); + Handles.DrawSolidArc(joint.transform.position, normalYDirection, fromYDirection, -joint.angularYLimit.limit, + 0.1f); + Handles.color = Color.blue * new Color(1, 1, 1, 0.2f); + Handles.DrawSolidArc(joint.transform.position, normalZDirection, fromZDirection, -joint.angularZLimit.limit, + 0.1f); + + // Draw handles taking into account joint axis + Matrix4x4 handleXMatrix = Matrix4x4.TRS( + joint.transform.position, + Quaternion.LookRotation(fromXDirection, normalXDirection), + Vector3.one); + + Matrix4x4 handleYMatrix = Matrix4x4.TRS( + joint.transform.position, + Quaternion.LookRotation(fromYDirection, normalYDirection), + Vector3.one); + + Matrix4x4 handleZMatrix = Matrix4x4.TRS( + joint.transform.position, + Quaternion.LookRotation(fromZDirection, normalZDirection), + Vector3.one); + + + Handles.color = Color.white; + + using (new Handles.DrawingScope(handleXMatrix)) + { + arcHandleXLow.DrawHandle(); + arcHandleXHigh.DrawHandle(); + } + + using (new Handles.DrawingScope(handleYMatrix)) + arcHandleY.DrawHandle(); + + using (new Handles.DrawingScope(handleZMatrix)) + arcHandleZ.DrawHandle(); + + // Dispose values to arc handles with clamped values + Context.jointLowXLimit = Mathf.Clamp(arcHandleXLow.angle, 0, 180); + Context.jointHighXLimit = Mathf.Clamp(-arcHandleXHigh.angle, 0, 180); + Context.jointYLimit = Mathf.Clamp(arcHandleY.angle, 0, 180); + Context.jointZLimit = Mathf.Clamp(arcHandleZ.angle, 0, 180); + + Update(); + } + private void VisualizeJointLimits(ConfigurableJoint joint, float radius = 0.1f, float opacity = 0.25f) + { + if (!joint) return; + + Vector3 normalXDirection = joint.transform.TransformDirection(joint.axis); + Vector3 fromXDirection = Vector3.Cross(joint.transform.TransformDirection(joint.axis), joint.transform.up); + + Vector3 normalYDirection = joint.transform.TransformDirection(Vector3.Cross(joint.axis, -Vector3.forward)); + Vector3 fromYDirection = Vector3.Cross(joint.transform.TransformDirection(joint.axis), normalYDirection); + + Vector3 normalZDirection = joint.transform.TransformDirection(Vector3.Cross(joint.axis, -Vector3.up)); + Vector3 fromZDirection = Vector3.Cross(joint.transform.TransformDirection(joint.axis), normalZDirection); + + // Angular X Limits + Handles.color = Color.red * new Color(1, 1, 1, 1); + Handles.DrawDottedLine(joint.transform.position, joint.transform.position + normalXDirection * radius / 2, + 5); + Handles.color = Color.red * new Color(1, 1, 1, opacity); + Handles.DrawSolidArc(joint.transform.position, normalXDirection, fromXDirection, + -joint.lowAngularXLimit.limit, radius); + Handles.DrawSolidArc(joint.transform.position, normalXDirection, fromXDirection, + -joint.highAngularXLimit.limit, radius); + + // Angular Y Limits + Handles.color = Color.green * new Color(1, 1, 1, 1); + Handles.DrawDottedLine(joint.transform.position, joint.transform.position + normalYDirection * radius / 2, + 5); + Handles.color = Color.green * new Color(1, 1, 1, opacity); + Handles.DrawSolidArc(joint.transform.position, normalYDirection, fromYDirection, joint.angularYLimit.limit, + radius); + Handles.DrawSolidArc(joint.transform.position, normalYDirection, fromYDirection, -joint.angularYLimit.limit, + radius); + + // Angular Z Limits + Handles.color = Color.blue * new Color(1, 1, 1, 1); + Handles.DrawDottedLine(joint.transform.position, joint.transform.position + normalZDirection * radius / 2, + 5); + Handles.color = Color.blue * new Color(1, 1, 1, opacity); + Handles.DrawSolidArc(joint.transform.position, normalZDirection, fromZDirection, joint.angularZLimit.limit, + radius); + Handles.DrawSolidArc(joint.transform.position, normalZDirection, fromZDirection, -joint.angularZLimit.limit, + radius); + } + + public override void Update() + { + if (!SelectedComponent) return; + + ConfigurableJoint lastSelectedJoint = SelectedComponent as ConfigurableJoint; + + Context.jointAxis.Normalize(); + + var lowAngularXLimit = lastSelectedJoint.lowAngularXLimit; + var highAngularXLimit = lastSelectedJoint.highAngularXLimit; + var angularYLimit = lastSelectedJoint.angularYLimit; + var angularZLimit = lastSelectedJoint.angularZLimit; + + lowAngularXLimit.limit = -Context.jointLowXLimit; + highAngularXLimit.limit = Context.jointHighXLimit; + angularYLimit.limit = Context.jointYLimit; + angularZLimit.limit = Context.jointZLimit; + + lastSelectedJoint.lowAngularXLimit = lowAngularXLimit; + lastSelectedJoint.highAngularXLimit = highAngularXLimit; + lastSelectedJoint.angularYLimit = angularYLimit; + lastSelectedJoint.angularZLimit = angularZLimit; + lastSelectedJoint.axis = Context.jointAxis; + } + + public override void Select(Component component) + { + base.Select(component); + ConfigurableJoint lastSelectedJoint = SelectedComponent as ConfigurableJoint; + + // Obtain original values + Context.jointAxis = lastSelectedJoint.axis; + Context.jointLowXLimit = -lastSelectedJoint.lowAngularXLimit.limit; + Context.jointHighXLimit = lastSelectedJoint.highAngularXLimit.limit; + Context.jointYLimit = lastSelectedJoint.angularYLimit.limit; + Context.jointZLimit = lastSelectedJoint.angularZLimit.limit; + } + + public override void Delete() + { + Undo.DestroyObjectImmediate(SelectedComponent.gameObject.GetComponent()); + base.Delete(); + } + } +} \ No newline at end of file diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/ConfigurableJointComponentState.cs.meta b/Gann4Games/Ragdoll Factory/Scripts/States/ConfigurableJointComponentState.cs.meta new file mode 100644 index 0000000..f175553 --- /dev/null +++ b/Gann4Games/Ragdoll Factory/Scripts/States/ConfigurableJointComponentState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f5174eeec86642f68f512425bd0816d8 +timeCreated: 1689048929 \ No newline at end of file diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs b/Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs new file mode 100644 index 0000000..a292c68 --- /dev/null +++ b/Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs @@ -0,0 +1,77 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace Gann4Games.RagdollFactory.States +{ + public abstract class RFComponentState + { + protected RagdollFactory Context { get; set; } + public List ComponentList { get; protected set; } + public Component SelectedComponent { get; protected set; } + public Component HighlightedComponent { get; protected set; } + + public bool IsSelected(Component component) + { + return SelectedComponent == component; + } + + public RFComponentState(RagdollFactory context) + { + Context = context; + } + + public abstract void Create(); + public abstract void ConvertTo(Component component); + public abstract void DrawGUI(); + public abstract void Update(); + + /// + /// Deletes the selected component and removes it from the history. + /// + public virtual void Delete() + { + ComponentList.Remove(SelectedComponent); + Undo.DestroyObjectImmediate(SelectedComponent); + } + + /// + /// Deletes the game object from the selected component and removes it from the history. + /// + public virtual void DeleteSelectedGameObject() + { + ComponentList.Remove(SelectedComponent); + Undo.DestroyObjectImmediate(SelectedComponent.gameObject); + } + + /// + /// Selects the component, if it isn't on the history, add it. + /// + /// + public virtual void Select(Component component) + { + if(!ComponentList.Contains(component)) + ComponentList.Add(component); + + SelectedComponent = component; + } + + public virtual void Deselect() + { + SelectedComponent = null; + } + + protected T GetOrAddComponent(GameObject target) where T : Component + { + if(target.TryGetComponent(out T component)) + return component; + + T comp = target.AddComponent(); + Undo.RegisterCreatedObjectUndo(comp, "Created component " + comp.name); + return comp; + } + } +} \ No newline at end of file diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs.meta b/Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs.meta new file mode 100644 index 0000000..080b10e --- /dev/null +++ b/Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 83101509a0814b47af8e53ac4c6d4410 +timeCreated: 1689048697 \ No newline at end of file diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/RigidbodyComponentState.cs b/Gann4Games/Ragdoll Factory/Scripts/States/RigidbodyComponentState.cs new file mode 100644 index 0000000..a5a96c5 --- /dev/null +++ b/Gann4Games/Ragdoll Factory/Scripts/States/RigidbodyComponentState.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace Gann4Games.RagdollFactory.States +{ + public class RigidbodyComponentState : RFComponentState + { + private bool Pressed(Rigidbody rb) + { + return Handles.Button( + rb.transform.position, + SceneView.currentDrawingSceneView.camera.transform.rotation, + rb.mass * Context.discRadius, + rb.mass * Context.discRadius, + Handles.DotHandleCap + ); + } + + public RigidbodyComponentState(RagdollFactory components) : base(components) + { + ComponentList = new List(); + ComponentList.AddRange(Context.GetComponentsInChildren()); + } + + public override void Create() + { + Rigidbody rb = GetOrAddComponent(Context.selectedBoneB.gameObject); + Select(rb); + Update(); + } + + public override void ConvertTo(Component component) + { + throw new System.NotImplementedException(); + } + + public override void DrawGUI() + { + foreach(Rigidbody rb in ComponentList.ToArray()) + { + Handles.color = Context.normalColor * (rb.isKinematic ? Color.red : Color.green); + Handles.color = IsSelected(rb) ? Context.selectedColor : Handles.color; + + switch (Context.actionTypeOnClick) + { + case RagdollFactory.ActionTypeOnClick.Create: + Handles.DrawSolidDisc( + rb.transform.position, + SceneView.currentDrawingSceneView.camera.transform.forward, + rb.mass * Context.discRadius); + break; + case RagdollFactory.ActionTypeOnClick.Select: + if (Pressed(rb)) + Select(rb); + break; + case RagdollFactory.ActionTypeOnClick.Delete: + if (Pressed(rb)) + { + Select(rb); + Delete(); + } + break; + } + } + } + + public override void Update() + { + if (!SelectedComponent) return; + + Rigidbody rb = (Rigidbody) SelectedComponent; + rb.mass = Context.rigidbodyMass; + rb.drag = Context.rigidbodyDrag; + rb.angularDrag = Context.rigidbodyAngularDrag; + rb.useGravity = Context.rigidbodyUseGravity; + rb.isKinematic = Context.rigidbodyIsKinematic; + } + + public override void Select(Component component) + { + base.Select(component); + Rigidbody rb = SelectedComponent as Rigidbody; + Context.rigidbodyMass = rb.mass; + Context.rigidbodyDrag = rb.drag; + Context.rigidbodyAngularDrag = rb.angularDrag; + Context.rigidbodyUseGravity = rb.useGravity; + Context.rigidbodyIsKinematic = rb.isKinematic; + } + } +} \ No newline at end of file diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/RigidbodyComponentState.cs.meta b/Gann4Games/Ragdoll Factory/Scripts/States/RigidbodyComponentState.cs.meta new file mode 100644 index 0000000..68d5a85 --- /dev/null +++ b/Gann4Games/Ragdoll Factory/Scripts/States/RigidbodyComponentState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b7ef164a09004d5298da35f4796aeeca +timeCreated: 1689048941 \ No newline at end of file From e8e7017d8dcec31fced342ec82c820942e5014d5 Mon Sep 17 00:00:00 2001 From: Santiago Ariel Mansilla <56666653+Gann4Life@users.noreply.github.com> Date: Tue, 11 Jul 2023 22:41:57 -0300 Subject: [PATCH 2/3] Secondary fixes, stable build --- .../Editor/RagdollFactoryEditor.SceneGUI.cs | 2 +- .../Scripts/Editor/RagdollFactoryEditor.cs | 11 ++++++- .../Ragdoll Factory/Scripts/RagdollFactory.cs | 19 +++++++++-- .../States/BoxColliderComponentState.cs | 27 ++++++++------- .../States/CapsuleColliderComponentState.cs | 33 ++++++++++--------- .../States/ConfigurableJointComponentState.cs | 23 ++++++------- .../Scripts/States/RFComponentState.cs | 26 +++++++++++---- .../Scripts/States/RigidbodyComponentState.cs | 9 +++-- 8 files changed, 94 insertions(+), 56 deletions(-) diff --git a/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.SceneGUI.cs b/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.SceneGUI.cs index 0b8842a..bc055e5 100644 --- a/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.SceneGUI.cs +++ b/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.SceneGUI.cs @@ -14,7 +14,7 @@ private void OnSceneGUI() CheckForInput(); - _target.CurrentComponent.DrawGUI(); + _target.CurrentComponent.DrawSceneGUI(); switch (_target.actionTypeOnClick) { case RagdollFactory.ActionTypeOnClick.Create: diff --git a/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.cs b/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.cs index b09fda0..4593125 100644 --- a/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.cs +++ b/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.cs @@ -1,3 +1,4 @@ +using Gann4Games.RagdollFactory.States; using UnityEditor; using UnityEngine; using UnityEngine.Windows; @@ -124,7 +125,7 @@ private void DrawInspectorTabs() new(iconDelete, "Delete") }; - _target.componentType = (RagdollFactory.ComponentType)GUILayout.Toolbar((int)_target.componentType, componentTabs); + _target.componentType = (RagdollFactory.ComponentType)GUILayout.Toolbar((int)_target.CurrentStateIndex(), componentTabs); _target.SetState(_target.States[(int)_target.componentType]); _target.actionTypeOnClick = (RagdollFactory.ActionTypeOnClick)GUILayout.Toolbar((int)_target.actionTypeOnClick, actionTypeTabs); @@ -152,6 +153,8 @@ private void DrawCurrentComponentProperties() } private void DrawRigidbodyProperties() { + if(!(_target.CurrentComponent is RigidbodyComponentState)) return; + EditorGUILayout.PropertyField(_rigidbodyMass); EditorGUILayout.PropertyField(_rigidbodyDrag); EditorGUILayout.PropertyField(_rigidbodyAngularDrag); @@ -163,6 +166,8 @@ private void DrawRigidbodyProperties() } private void DrawJointProperties() { + if(!(_target.CurrentComponent is ConfigurableJointComponentState)) return; + EditorGUILayout.PropertyField(_jointAxis); EditorGUILayout.PropertyField(_jointLowXLimit); EditorGUILayout.PropertyField(_jointHighXLimit); @@ -174,6 +179,8 @@ private void DrawJointProperties() } private void DrawCapsuleColliderProperties() { + if(!(_target.CurrentComponent is CapsuleColliderComponentState)) return; + EditorGUILayout.PropertyField(_capsuleLength); EditorGUILayout.PropertyField(_capsuleRadius); @@ -186,6 +193,8 @@ private void DrawCapsuleColliderProperties() } private void DrawBoxColliderProperties() { + if(!(_target.CurrentComponent is BoxColliderComponentState)) return; + EditorGUILayout.PropertyField(_boxLength); EditorGUILayout.PropertyField(_boxWidth); EditorGUILayout.PropertyField(_boxDepth); diff --git a/Gann4Games/Ragdoll Factory/Scripts/RagdollFactory.cs b/Gann4Games/Ragdoll Factory/Scripts/RagdollFactory.cs index d596c9c..06dcde8 100644 --- a/Gann4Games/Ragdoll Factory/Scripts/RagdollFactory.cs +++ b/Gann4Games/Ragdoll Factory/Scripts/RagdollFactory.cs @@ -69,7 +69,22 @@ public enum ComponentType Capsule, Box, ConfigurableJoint, Rigidbody } - public void SetState(RFComponentState state) => CurrentComponent = state; + public void SetState(RFComponentState state) + { + CurrentComponent = state; + } + + /// + /// Returns the index of a state + /// + /// + /// + private int GetStateIndex(RFComponentState state) => Array.IndexOf(States, state); + + /// + /// Returns the current state as an integer + /// + public int CurrentStateIndex() => GetStateIndex(CurrentComponent); public void DeselectBones() { @@ -79,7 +94,7 @@ public void DeselectBones() private void OnValidate() { - CurrentComponent.Update(); + CurrentComponent?.Update(); } private void OnEnable() diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/BoxColliderComponentState.cs b/Gann4Games/Ragdoll Factory/Scripts/States/BoxColliderComponentState.cs index 1f2eb3a..535efc8 100644 --- a/Gann4Games/Ragdoll Factory/Scripts/States/BoxColliderComponentState.cs +++ b/Gann4Games/Ragdoll Factory/Scripts/States/BoxColliderComponentState.cs @@ -9,12 +9,13 @@ public class BoxColliderComponentState : RFComponentState { private bool Pressed(BoxCollider col) { + var size = Mathf.Max(col.size.x, col.size.y); return Handles.Button( col.transform.position, col.transform.rotation, - Mathf.Max(col.size.x, col.size.y), - 0.1f, - Handles.CubeHandleCap + size, + size, + Handles.RectangleHandleCap ); } public BoxColliderComponentState(RagdollFactory context) : base(context) @@ -49,7 +50,7 @@ public override void Create() public override void ConvertTo(Component component) { - if (component is BoxCollider) + if (component is CapsuleCollider) { BoxCollider col = SelectedComponent as BoxCollider; GameObject collisionObject = col.gameObject; @@ -73,20 +74,25 @@ public override void ConvertTo(Component component) Undo.RegisterCompleteObjectUndo(collisionObject, "Converted Box Collider to Capsule Collider"); + Context.SetState(Context.CapsuleColliderComponentState); Context.CapsuleColliderComponentState.Select(capsuleCollider); } } - public override void DrawGUI() + public override void DrawSceneGUI() { foreach(BoxCollider collider in ComponentList.ToArray()) { if (!collider) continue; Handles.color = IsSelected(collider) ? Context.selectedColor : Context.normalColor; - if(IsSelected(collider)) - collider.transform.position = Handles.PositionHandle(collider.transform.position, collider.transform.rotation); - + if (IsSelected(collider)) + { + Undo.RecordObject(collider.transform, "Moved Box Collider"); + collider.transform.position = + Handles.PositionHandle(collider.transform.position, collider.transform.rotation); + } + switch (Context.actionTypeOnClick) { case RagdollFactory.ActionTypeOnClick.Select: @@ -95,10 +101,7 @@ public override void DrawGUI() break; case RagdollFactory.ActionTypeOnClick.Delete: if (Pressed(collider)) - { - Select(collider); - Delete(); - } + Delete(collider); break; } } diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/CapsuleColliderComponentState.cs b/Gann4Games/Ragdoll Factory/Scripts/States/CapsuleColliderComponentState.cs index cb33bf5..b35ee29 100644 --- a/Gann4Games/Ragdoll Factory/Scripts/States/CapsuleColliderComponentState.cs +++ b/Gann4Games/Ragdoll Factory/Scripts/States/CapsuleColliderComponentState.cs @@ -9,12 +9,13 @@ public class CapsuleColliderComponentState : RFComponentState { private bool Pressed(CapsuleCollider col) { + var size = col.radius*2; return Handles.Button( col.transform.position, - Quaternion.identity, - col.radius, - 0.1f, - Handles.SphereHandleCap + col.transform.rotation, + size, + size, + Handles.CircleHandleCap ); } @@ -73,24 +74,27 @@ public override void ConvertTo(Component component) boxCollider.center = Vector3.forward * height; boxCollider.size = new Vector3(radius, radius, distance); - Context.BoxColliderComponentState.Select(boxCollider); - Undo.RegisterCompleteObjectUndo(collisionObject, "Converted Capsule Collider to Box Collider"); - + Context.SetState(Context.BoxColliderComponentState); + Context.BoxColliderComponentState.Select(boxCollider); } } - public override void DrawGUI() + public override void DrawSceneGUI() { foreach(CapsuleCollider collider in ComponentList.ToArray()) { if (!collider) continue; - Handles.color = IsSelected(collider) ? Context.selectedColor : Context.normalColor; + Handles.color = Color.white; + + if (IsSelected(collider)) + { + Undo.RecordObject(collider.transform, "Moved Capsule Collider"); + collider.transform.position = + Handles.PositionHandle(collider.transform.position, collider.transform.rotation); + } - if(IsSelected(collider)) - collider.transform.position = Handles.PositionHandle(collider.transform.position, collider.transform.rotation); - switch (Context.actionTypeOnClick) { case RagdollFactory.ActionTypeOnClick.Select: @@ -99,10 +103,7 @@ public override void DrawGUI() break; case RagdollFactory.ActionTypeOnClick.Delete: if (Pressed(collider)) - { - Select(collider); - Delete(); - } + Delete(collider); break; } } diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/ConfigurableJointComponentState.cs b/Gann4Games/Ragdoll Factory/Scripts/States/ConfigurableJointComponentState.cs index 4fa30df..c2d0ce4 100644 --- a/Gann4Games/Ragdoll Factory/Scripts/States/ConfigurableJointComponentState.cs +++ b/Gann4Games/Ragdoll Factory/Scripts/States/ConfigurableJointComponentState.cs @@ -73,10 +73,12 @@ public override void ConvertTo(Component component) throw new System.NotImplementedException(); } - public override void DrawGUI() + public override void DrawSceneGUI() { foreach (ConfigurableJoint joint in ComponentList.ToArray()) { + if (!joint) continue; + if(IsSelected(joint)) DisplayJointHandles(joint); else VisualizeJointLimits(joint); @@ -92,10 +94,7 @@ public override void DrawGUI() case RagdollFactory.ActionTypeOnClick.Delete: DisplayJointStatus(joint); if (Pressed(joint)) - { - Select(joint); - Delete(); - } + Delete(joint); break; } } @@ -115,8 +114,6 @@ private void DisplayJointStatus(ConfigurableJoint joint) private void DisplayJointHandles(ConfigurableJoint joint) { - if (!joint) return; - Vector3 normalXDirection = joint.transform.TransformDirection(joint.axis); Vector3 fromXDirection = Vector3.Cross(joint.transform.TransformDirection(joint.axis), joint.transform.up); @@ -126,7 +123,7 @@ private void DisplayJointHandles(ConfigurableJoint joint) Vector3 normalZDirection = joint.transform.TransformDirection(Vector3.Cross(joint.axis, -Vector3.up)); Vector3 fromZDirection = Vector3.Cross(joint.transform.TransformDirection(joint.axis), normalZDirection); - // Load arc values + // Load arc values from inspector arcHandleXLow.angle = Context.jointLowXLimit; arcHandleXHigh.angle = -Context.jointHighXLimit; arcHandleY.angle = Context.jointYLimit; @@ -179,15 +176,14 @@ private void DisplayJointHandles(ConfigurableJoint joint) // Dispose values to arc handles with clamped values Context.jointLowXLimit = Mathf.Clamp(arcHandleXLow.angle, 0, 180); Context.jointHighXLimit = Mathf.Clamp(-arcHandleXHigh.angle, 0, 180); - Context.jointYLimit = Mathf.Clamp(arcHandleY.angle, 0, 180); - Context.jointZLimit = Mathf.Clamp(arcHandleZ.angle, 0, 180); + // By using the absolute values we guarantee that the handle can be used in both directions + Context.jointYLimit = Mathf.Clamp(Mathf.Abs(arcHandleY.angle), 0, 180); + Context.jointZLimit = Mathf.Clamp(Mathf.Abs(arcHandleZ.angle), 0, 180); Update(); } private void VisualizeJointLimits(ConfigurableJoint joint, float radius = 0.1f, float opacity = 0.25f) { - if (!joint) return; - Vector3 normalXDirection = joint.transform.TransformDirection(joint.axis); Vector3 fromXDirection = Vector3.Cross(joint.transform.TransformDirection(joint.axis), joint.transform.up); @@ -268,7 +264,8 @@ public override void Select(Component component) public override void Delete() { - Undo.DestroyObjectImmediate(SelectedComponent.gameObject.GetComponent()); + Rigidbody rb = SelectedComponent.gameObject.GetComponent(); + Context.RigidbodyComponentState.Delete(rb); base.Delete(); } } diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs b/Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs index a292c68..e6d580b 100644 --- a/Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs +++ b/Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs @@ -24,11 +24,9 @@ public RFComponentState(RagdollFactory context) Context = context; } + #region Actions public abstract void Create(); - public abstract void ConvertTo(Component component); - public abstract void DrawGUI(); - public abstract void Update(); - + /// /// Deletes the selected component and removes it from the history. /// @@ -37,7 +35,16 @@ public virtual void Delete() ComponentList.Remove(SelectedComponent); Undo.DestroyObjectImmediate(SelectedComponent); } - + /// + /// Selects an element and then deletes it. + /// + /// + public void Delete(Component component) + { + Select(component); + Delete(); + } + /// /// Deletes the game object from the selected component and removes it from the history. /// @@ -46,7 +53,7 @@ public virtual void DeleteSelectedGameObject() ComponentList.Remove(SelectedComponent); Undo.DestroyObjectImmediate(SelectedComponent.gameObject); } - + /// /// Selects the component, if it isn't on the history, add it. /// @@ -64,6 +71,13 @@ public virtual void Deselect() SelectedComponent = null; } + public abstract void ConvertTo(Component component); + #endregion + + public abstract void Update(); + + public abstract void DrawSceneGUI(); + protected T GetOrAddComponent(GameObject target) where T : Component { if(target.TryGetComponent(out T component)) diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/RigidbodyComponentState.cs b/Gann4Games/Ragdoll Factory/Scripts/States/RigidbodyComponentState.cs index a5a96c5..4624884 100644 --- a/Gann4Games/Ragdoll Factory/Scripts/States/RigidbodyComponentState.cs +++ b/Gann4Games/Ragdoll Factory/Scripts/States/RigidbodyComponentState.cs @@ -35,10 +35,12 @@ public override void ConvertTo(Component component) throw new System.NotImplementedException(); } - public override void DrawGUI() + public override void DrawSceneGUI() { foreach(Rigidbody rb in ComponentList.ToArray()) { + if(!rb) continue; + Handles.color = Context.normalColor * (rb.isKinematic ? Color.red : Color.green); Handles.color = IsSelected(rb) ? Context.selectedColor : Handles.color; @@ -56,10 +58,7 @@ public override void DrawGUI() break; case RagdollFactory.ActionTypeOnClick.Delete: if (Pressed(rb)) - { - Select(rb); - Delete(); - } + Delete(rb); break; } } From fd6d4dcd255db84773ea6f7df189b45b2e82ff50 Mon Sep 17 00:00:00 2001 From: Santiago Ariel Mansilla <56666653+Gann4Life@users.noreply.github.com> Date: Wed, 12 Jul 2023 00:31:35 -0300 Subject: [PATCH 3/3] Handle resize, error prevention when converting colliders --- .../Scripts/Editor/RagdollFactoryEditor.SceneGUI.cs | 2 +- .../Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.cs | 1 + .../Scripts/States/BoxColliderComponentState.cs | 2 +- .../Scripts/States/CapsuleColliderComponentState.cs | 2 +- Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs | 3 +++ 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.SceneGUI.cs b/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.SceneGUI.cs index bc055e5..ca1c442 100644 --- a/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.SceneGUI.cs +++ b/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.SceneGUI.cs @@ -52,7 +52,7 @@ private void DrawSelectableBones() bone.position, SceneView.currentDrawingSceneView.camera.transform.rotation, _target.discRadius, - _target.discRadius*2, + _target.discRadius, Handles.CircleHandleCap ); diff --git a/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.cs b/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.cs index 4593125..d875184 100644 --- a/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.cs +++ b/Gann4Games/Ragdoll Factory/Scripts/Editor/RagdollFactoryEditor.cs @@ -135,6 +135,7 @@ private void DrawInspectorTabs() #region Property Drawing private void DrawCurrentComponentProperties() { + if(!_target.CurrentComponent.HasComponentSelected) return; switch (_target.componentType) { case RagdollFactory.ComponentType.Capsule: diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/BoxColliderComponentState.cs b/Gann4Games/Ragdoll Factory/Scripts/States/BoxColliderComponentState.cs index 535efc8..ba96540 100644 --- a/Gann4Games/Ragdoll Factory/Scripts/States/BoxColliderComponentState.cs +++ b/Gann4Games/Ragdoll Factory/Scripts/States/BoxColliderComponentState.cs @@ -9,7 +9,7 @@ public class BoxColliderComponentState : RFComponentState { private bool Pressed(BoxCollider col) { - var size = Mathf.Max(col.size.x, col.size.y); + var size = Mathf.Max(col.size.x, col.size.y) * 0.7f; return Handles.Button( col.transform.position, col.transform.rotation, diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/CapsuleColliderComponentState.cs b/Gann4Games/Ragdoll Factory/Scripts/States/CapsuleColliderComponentState.cs index b35ee29..7d03168 100644 --- a/Gann4Games/Ragdoll Factory/Scripts/States/CapsuleColliderComponentState.cs +++ b/Gann4Games/Ragdoll Factory/Scripts/States/CapsuleColliderComponentState.cs @@ -9,7 +9,7 @@ public class CapsuleColliderComponentState : RFComponentState { private bool Pressed(CapsuleCollider col) { - var size = col.radius*2; + var size = col.radius*1.2f; return Handles.Button( col.transform.position, col.transform.rotation, diff --git a/Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs b/Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs index e6d580b..bd16ee2 100644 --- a/Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs +++ b/Gann4Games/Ragdoll Factory/Scripts/States/RFComponentState.cs @@ -18,6 +18,8 @@ public bool IsSelected(Component component) { return SelectedComponent == component; } + + public bool HasComponentSelected => SelectedComponent != null; public RFComponentState(RagdollFactory context) { @@ -64,6 +66,7 @@ public virtual void Select(Component component) ComponentList.Add(component); SelectedComponent = component; + EditorGUIUtility.PingObject(SelectedComponent); } public virtual void Deselect()