using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; namespace Module.Inspector.Editor.Utilities { public static class EditorHandleUtility { private static readonly Color DEFAULT_AXIS_COLOR = new Color(0.7f, 0.7f, 0.7f, 0.93f); private static readonly Color X_AXIS_COLOR = new Color(0.85882354f, 0.24313726f, 0.11372549f, 0.93f); private static readonly Color Y_AXIS_COLOR = new Color(0.6039216f, 0.9529412f, 0.28235295f, 0.93f); private static readonly Color Z_AXIS_COLOR = new Color(0.22745098f, 0.47843137f, 0.972549f, 0.93f); private static readonly Color EDGE_COLOR = new Color(0.6039216f, 0.9529412f, 0.28235295f, 0.93f); private static readonly Color EDGE_COLOR2 = new Color(0.6039216f, 0.9529412f, 0.28235295f, 0.4f); private static readonly Quaternion CIRCLE_CAP_ROTATION = Quaternion.Euler(90f, 0f, 0f); private static readonly Quaternion CIRCLE_CAP_ROTATION2 = Quaternion.Euler(0f, 90f, 0f); private static readonly Quaternion RECTANGLE_CAP_ROTATION = Quaternion.Euler(90f, 0f, 0f); public static Vector2 RectHandle(Vector3 worldPosition, Quaternion worldRotation, Vector2 rectSize, EAxis axii) { float size = HandleUtility.GetHandleSize(worldPosition); float dotSize = size * 0.125f; Handles.matrix = Matrix4x4.TRS(worldPosition, worldRotation * RECTANGLE_CAP_ROTATION, Vector3.one); Color color = Handles.color; Handles.color = EDGE_COLOR; Handles.DrawWireCube(Vector3.zero, rectSize); if ((axii & EAxis.X) != 0) { var point = new Vector3(rectSize.x * 0.5f, 0f, 0f); Handles.color = DEFAULT_AXIS_COLOR; point = Handles.Slider(point, Vector3.right, dotSize, Handles.CubeHandleCap, 0.01f); rectSize.x = Mathf.Max(point.x, 0f) * 2f; } if ((axii & EAxis.Y) != 0) { var point = new Vector3(0f, rectSize.y * 0.5f, 0f); Handles.color = DEFAULT_AXIS_COLOR; point = Handles.Slider(point, Vector3.up, dotSize, Handles.CubeHandleCap, 0.01f); rectSize.y = Mathf.Max(point.y, 0f) * 2f; } Handles.color = color; Handles.matrix = Matrix4x4.identity; return rectSize; } public static float CircleHandle(Vector3 worldPosition, Quaternion worldRotation, float circleSize) { float size = HandleUtility.GetHandleSize(worldPosition); float dotSize = size * 0.125f; Handles.matrix = Matrix4x4.TRS(worldPosition, worldRotation, Vector3.one); Color color = Handles.color; Handles.color = EDGE_COLOR; Handles.CircleHandleCap(-1, Vector3.zero, CIRCLE_CAP_ROTATION, circleSize, EventType.Repaint); var point = new Vector3(circleSize, 0f, 0f); Handles.color = DEFAULT_AXIS_COLOR; point = Handles.Slider(point, Vector3.right, dotSize, Handles.CubeHandleCap, 0.01f); circleSize = Mathf.Max(point.x, 0f); Handles.color = color; Handles.matrix = Matrix4x4.identity; return circleSize; } public static float SphereHandle(Vector3 worldPosition, Quaternion worldRotation, float sphereSize) { float size = HandleUtility.GetHandleSize(worldPosition); float dotSize = size * 0.125f; Handles.matrix = Matrix4x4.TRS(worldPosition, worldRotation, Vector3.one); Color color = Handles.color; Handles.color = EDGE_COLOR; Handles.CircleHandleCap(-1, Vector3.zero, CIRCLE_CAP_ROTATION, sphereSize, EventType.Repaint); Handles.color = EDGE_COLOR2; Handles.CircleHandleCap(-1, Vector3.zero, Quaternion.identity, sphereSize, EventType.Repaint); Handles.CircleHandleCap(-1, Vector3.zero, CIRCLE_CAP_ROTATION2, sphereSize, EventType.Repaint); var point = new Vector3(sphereSize, 0f, 0f); Handles.color = DEFAULT_AXIS_COLOR; point = Handles.Slider(point, Vector3.right, dotSize, Handles.CubeHandleCap, 0.01f); sphereSize = Mathf.Max(point.x, 0f); Handles.color = color; Handles.matrix = Matrix4x4.identity; return sphereSize; } public static Vector3 BoxHandle(Vector3 worldPosition, Quaternion worldRotation, Vector3 boxSize, EAxis axii = EAxis.All) { float size = HandleUtility.GetHandleSize(worldPosition); float dotSize = size * 0.125f; Handles.matrix = Matrix4x4.TRS(worldPosition, worldRotation, Vector3.one); Color color = Handles.color; Handles.color = EDGE_COLOR; Handles.DrawWireCube(Vector3.zero, boxSize); if ((axii & EAxis.X) != 0) { var point = new Vector3(boxSize.x * 0.5f, 0f, 0f); Handles.color = DEFAULT_AXIS_COLOR; point = Handles.Slider(point, Vector3.right, dotSize, Handles.CubeHandleCap, 0.01f); boxSize.x = Mathf.Max(point.x, 0f) * 2f; } if ((axii & EAxis.Y) != 0) { var point = new Vector3(0f, boxSize.y * 0.5f, 0f); Handles.color = DEFAULT_AXIS_COLOR; point = Handles.Slider(point, Vector3.up, dotSize, Handles.CubeHandleCap, 0.01f); boxSize.y = Mathf.Max(point.y, 0f) * 2f; } if ((axii & EAxis.Z) != 0) { var point = new Vector3(0f, 0f, boxSize.z * 0.5f); Handles.color = DEFAULT_AXIS_COLOR; point = Handles.Slider(point, Vector3.forward, dotSize, Handles.CubeHandleCap, 0.01f); boxSize.z = Mathf.Max(point.z, 0f) * 2f; } Handles.color = color; Handles.matrix = Matrix4x4.identity; return boxSize; } public static Vector3 PositionHandle(Vector3 worldPosition, Quaternion worldRotation, EAxis axii = EAxis.All) { float size = HandleUtility.GetHandleSize(worldPosition); float twoAxisSize = size * 0.1f; Color color = Handles.color; if ((axii & (EAxis.X | EAxis.Y)) == (EAxis.X | EAxis.Y)) { Handles.color = Z_AXIS_COLOR; Vector3 offset = worldRotation * new Vector3(twoAxisSize, twoAxisSize, 0f); worldPosition = Handles.Slider2D(worldPosition + offset, worldRotation * Vector3.forward, worldRotation * Vector3.right, worldRotation * Vector3.up, twoAxisSize, Handles.RectangleHandleCap, 0.01f) - offset; } if ((axii & (EAxis.X | EAxis.Z)) == (EAxis.X | EAxis.Z)) { Handles.color = Y_AXIS_COLOR; Vector3 offset = worldRotation * new Vector3(twoAxisSize, 0f, twoAxisSize); worldPosition = Handles.Slider2D(worldPosition + offset, worldRotation * Vector3.up, worldRotation * Vector3.right, worldRotation * Vector3.forward, twoAxisSize, Handles.RectangleHandleCap, 0.01f) - offset; } if ((axii & (EAxis.Y | EAxis.Z)) == (EAxis.Y | EAxis.Z)) { Handles.color = X_AXIS_COLOR; Vector3 offset = worldRotation * new Vector3(0f, twoAxisSize, twoAxisSize); worldPosition = Handles.Slider2D(worldPosition + offset, worldRotation * Vector3.right, worldRotation * Vector3.up, worldRotation * Vector3.forward, twoAxisSize, Handles.RectangleHandleCap, 0.01f) - offset; } if ((axii & EAxis.X) != 0) { Handles.color = X_AXIS_COLOR; worldPosition = Handles.Slider(worldPosition, worldRotation * Vector3.right, size, Handles.ArrowHandleCap, 0.01f); } if ((axii & EAxis.Y) != 0) { Handles.color = Y_AXIS_COLOR; worldPosition = Handles.Slider(worldPosition, worldRotation * Vector3.up, size, Handles.ArrowHandleCap, 0.01f); } if ((axii & EAxis.Z) != 0) { Handles.color = Z_AXIS_COLOR; worldPosition = Handles.Slider(worldPosition, worldRotation * Vector3.forward, size, Handles.ArrowHandleCap, 0.01f); } Handles.color = color; return worldPosition; } public static Quaternion RotationHandle(Vector3 worldPosition, Quaternion worldRotation) { Color color = Handles.color; worldRotation = Handles.RotationHandle(worldRotation, worldPosition); Handles.color = color; return worldRotation; } public static bool TryGetWorldPosition(SerializedProperty serializedProperty, string fieldPosition, Space space, out Vector3 worldPosition) { SerializedProperty spPosition = serializedProperty.GetSibling(fieldPosition); if (spPosition != null) { worldPosition = GetWorldPosition(spPosition, space); } else { Transform transform = GetTransform(serializedProperty); worldPosition = transform != null ? transform.position : Vector3.zero; } return spPosition != null; } public static Vector3 GetWorldPosition(SerializedProperty serializedProperty, string fieldPosition, Space space) { Vector3 worldPosition = Vector3.zero; SerializedProperty spPosition = serializedProperty.GetSibling(fieldPosition); if (spPosition != null) { worldPosition = GetWorldPosition(spPosition, space); } else { Transform transform = GetTransform(serializedProperty); if (transform != null) worldPosition = transform.position; } return worldPosition; } private static Transform GetTransform(SerializedProperty serializedProperty) { Object target = serializedProperty.serializedObject.targetObject; if (target is Component component) return component.transform; return null; } public static Vector3 GetWorldPosition(SerializedProperty serializedProperty, Space space) { Vector3 worldPosition = Vector3.zero; var requireObjectTransform = true; switch (serializedProperty.propertyType) { case SerializedPropertyType.Integer: worldPosition = new Vector3(serializedProperty.intValue, serializedProperty.intValue, serializedProperty.intValue); break; case SerializedPropertyType.Float: worldPosition = new Vector3(serializedProperty.floatValue, serializedProperty.floatValue, serializedProperty.floatValue); break; case SerializedPropertyType.ObjectReference: if (serializedProperty.objectReferenceValue != null && serializedProperty.objectReferenceValue is Transform transform) { worldPosition = transform.position; requireObjectTransform = false; } break; case SerializedPropertyType.Vector2: worldPosition = serializedProperty.vector2Value; break; case SerializedPropertyType.Vector3: worldPosition = serializedProperty.vector3Value; break; case SerializedPropertyType.Vector2Int: worldPosition = new Vector3(serializedProperty.vector2IntValue.x, serializedProperty.vector2IntValue.y, 0.0f); break; case SerializedPropertyType.Vector3Int: worldPosition = new Vector3(serializedProperty.vector3IntValue.x, serializedProperty.vector3IntValue.y, serializedProperty.vector3IntValue.z); break; } if (requireObjectTransform && space == Space.Self) { Transform objectTransform = GetTransform(serializedProperty); if (objectTransform != null) worldPosition = objectTransform.TransformPoint(worldPosition); } return worldPosition; } public static void SetWorldPosition(SerializedProperty serializedProperty, string fieldPosition, Vector3 worldPosition, Space space) { SerializedProperty spPosition = serializedProperty.GetSibling(fieldPosition); if (spPosition != null) { SetWorldPosition(spPosition, worldPosition, space); } else { Transform transform = GetTransform(serializedProperty); if (transform != null) transform.position = worldPosition; } } public static void SetWorldPosition(SerializedProperty serializedProperty, Vector3 worldPosition, Space space) { bool requireObjectTransform = !(serializedProperty.propertyType == SerializedPropertyType.ObjectReference && serializedProperty.objectReferenceValue != null && serializedProperty.objectReferenceValue is Transform); if (requireObjectTransform && space == Space.Self) { Transform objectTransform = GetTransform(serializedProperty); if (objectTransform != null) worldPosition = objectTransform.InverseTransformPoint(worldPosition); } switch (serializedProperty.propertyType) { case SerializedPropertyType.Integer: serializedProperty.intValue = Mathf.RoundToInt(worldPosition.x); break; case SerializedPropertyType.Float: serializedProperty.floatValue = worldPosition.x; break; case SerializedPropertyType.ObjectReference: if (serializedProperty.objectReferenceValue != null && serializedProperty.objectReferenceValue is Transform transform) transform.position = worldPosition; break; case SerializedPropertyType.Vector2: serializedProperty.vector2Value = worldPosition; break; case SerializedPropertyType.Vector3: serializedProperty.vector3Value = worldPosition; break; case SerializedPropertyType.Vector2Int: serializedProperty.vector2IntValue = new Vector2Int(Mathf.RoundToInt(worldPosition.x), Mathf.RoundToInt(worldPosition.y)); break; case SerializedPropertyType.Vector3Int: serializedProperty.vector3IntValue = new Vector3Int(Mathf.RoundToInt(worldPosition.x), Mathf.RoundToInt(worldPosition.y), Mathf.RoundToInt(worldPosition.z)); break; } } public static bool TryGetWorldRotation(SerializedProperty serializedProperty, string fieldRotation, Space space, out Quaternion worldRotation) { SerializedProperty spRotation = serializedProperty.GetSibling(fieldRotation); if (spRotation != null) { worldRotation = GetWorldRotation(spRotation, space); } else { Transform transform = GetTransform(serializedProperty); worldRotation = transform != null ? transform.rotation : Quaternion.identity; } return spRotation != null; } public static Quaternion GetWorldRotation(SerializedProperty serializedProperty, string fieldRotation, Space space) { Quaternion worldRotation = Quaternion.identity; SerializedProperty spRotation = serializedProperty.GetSibling(fieldRotation); if (spRotation != null) { worldRotation = GetWorldRotation(spRotation, space); } else { Transform transform = GetTransform(serializedProperty); if (transform != null) worldRotation = transform.rotation; } return worldRotation; } public static Quaternion GetWorldRotation(SerializedProperty serializedProperty, Space space) { Quaternion worldRotation = Quaternion.identity; var requireObjectTransform = true; switch (serializedProperty.propertyType) { case SerializedPropertyType.ObjectReference: if (serializedProperty.objectReferenceValue is Transform transform) { worldRotation = transform.rotation; requireObjectTransform = false; } break; case SerializedPropertyType.Quaternion: worldRotation = serializedProperty.quaternionValue; if (Mathf.Approximately(worldRotation.x, 0.0f) && Mathf.Approximately(worldRotation.y, 0.0f) && Mathf.Approximately(worldRotation.z, 0.0f) && Mathf.Approximately(worldRotation.w, 0.0f)) worldRotation = Quaternion.identity; break; } if (requireObjectTransform && space == Space.Self) { Transform objectTransform = GetTransform(serializedProperty); if (objectTransform != null) worldRotation = objectTransform.rotation * worldRotation; } return worldRotation; } public static void SetWorldRotation(SerializedProperty serializedProperty, string fieldRotation, Quaternion worldRotation, Space space) { SerializedProperty spRotation = serializedProperty.GetSibling(fieldRotation); if (spRotation != null) { SetWorldRotation(spRotation, worldRotation, space); } else { Transform transform = GetTransform(serializedProperty); if (transform != null) transform.rotation = worldRotation; } } public static void SetWorldRotation(SerializedProperty serializedProperty, Quaternion worldRotation, Space space) { bool requireObjectTransform = !(serializedProperty.propertyType == SerializedPropertyType.ObjectReference && serializedProperty.objectReferenceValue != null && serializedProperty.objectReferenceValue is Transform); if (requireObjectTransform && space == Space.Self) { Transform objectTransform = GetTransform(serializedProperty); if (objectTransform != null) worldRotation = Quaternion.Inverse(objectTransform.rotation) * worldRotation; } switch (serializedProperty.propertyType) { case SerializedPropertyType.ObjectReference: if (serializedProperty.objectReferenceValue is Transform transform) transform.rotation = worldRotation; break; case SerializedPropertyType.Quaternion: serializedProperty.quaternionValue = worldRotation; break; } } } }