// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik // #if UNITY_EDITOR && UNITY_IMGUI using UnityEditor; using UnityEditor.Animations; using UnityEngine; namespace Animancer.Editor { /// /// https://kybernetik.com.au/animancer/api/Animancer.Editor/ControllerTransitionDrawer [CustomPropertyDrawer(typeof(ControllerTransition<>), true)] [CustomPropertyDrawer(typeof(ControllerTransition), true)] public class ControllerTransitionDrawer : TransitionDrawer { /************************************************************************************************************************/ private readonly string[] Parameters; private readonly string[] ParameterPropertySuffixes; /************************************************************************************************************************/ /// Creates a new without any parameters. public ControllerTransitionDrawer() : base(ControllerTransition.ControllerFieldName) { } /// Creates a new and sets the . public ControllerTransitionDrawer(params string[] parameters) : base(ControllerTransition.ControllerFieldName) { Parameters = parameters; if (parameters == null) return; ParameterPropertySuffixes = new string[parameters.Length]; for (int i = 0; i < ParameterPropertySuffixes.Length; i++) { ParameterPropertySuffixes[i] = "." + parameters[i]; } } /************************************************************************************************************************/ /// protected override void DoChildPropertyGUI( ref Rect area, SerializedProperty rootProperty, SerializedProperty property, GUIContent label) { var path = property.propertyPath; if (ParameterPropertySuffixes != null) { var controllerProperty = rootProperty.FindPropertyRelative(MainPropertyName); if (controllerProperty.objectReferenceValue is AnimatorController controller) { for (int i = 0; i < ParameterPropertySuffixes.Length; i++) { if (path.EndsWith(ParameterPropertySuffixes[i])) { area.height = AnimancerGUI.LineHeight; DoParameterGUI(area, controller, property); return; } } } } EditorGUI.BeginChangeCheck(); base.DoChildPropertyGUI(ref area, rootProperty, property, label); // When the controller changes, validate all parameters. if (EditorGUI.EndChangeCheck() && Parameters != null && path.EndsWith(MainPropertyPathSuffix)) { if (property.objectReferenceValue is AnimatorController controller) { for (int i = 0; i < Parameters.Length; i++) { property = rootProperty.FindPropertyRelative(Parameters[i]); var parameterName = property.stringValue; // If a parameter is missing, assign it to the first float parameter. if (!HasFloatParameter(controller, parameterName)) { parameterName = GetFirstFloatParameterName(controller); if (!string.IsNullOrEmpty(parameterName)) property.stringValue = parameterName; } } } } } /************************************************************************************************************************/ /// Draws a dropdown menu to select the name of a parameter in the `controller`. protected void DoParameterGUI(Rect area, AnimatorController controller, SerializedProperty property) { var parameterName = property.stringValue; var parameters = controller.parameters; using (var label = PooledGUIContent.Acquire(property)) { var propertyLabel = EditorGUI.BeginProperty(area, label, property); var xMax = area.xMax; area.width = EditorGUIUtility.labelWidth; EditorGUI.PrefixLabel(area, propertyLabel); area.x += area.width; area.xMax = xMax; } var color = GUI.color; if (!HasFloatParameter(controller, parameterName)) GUI.color = AnimancerGUI.ErrorFieldColor; using (var label = PooledGUIContent.Acquire(parameterName)) { if (EditorGUI.DropdownButton(area, label, FocusType.Passive)) { property = property.Copy(); var menu = new GenericMenu(); for (int i = 0; i < parameters.Length; i++) { var parameter = parameters[i]; Serialization.AddPropertyModifierFunction(menu, property, parameter.name, parameter.type == AnimatorControllerParameterType.Float, (targetProperty) => { targetProperty.stringValue = parameter.name; }); } if (menu.GetItemCount() == 0) menu.AddDisabledItem(new("No Parameters")); menu.ShowAsContext(); } } GUI.color = color; EditorGUI.EndProperty(); } /************************************************************************************************************************/ private static bool HasFloatParameter(AnimatorController controller, string name) { if (string.IsNullOrEmpty(name)) return false; var parameters = controller.parameters; for (int i = 0; i < parameters.Length; i++) { var parameter = parameters[i]; if (parameter.type == AnimatorControllerParameterType.Float && parameter.name == name) { return true; } } return false; } /************************************************************************************************************************/ private static string GetFirstFloatParameterName(AnimatorController controller) { var parameters = controller.parameters; for (int i = 0; i < parameters.Length; i++) { var parameter = parameters[i]; if (parameter.type == AnimatorControllerParameterType.Float) { return parameter.name; } } return ""; } /************************************************************************************************************************/ } } #endif