SerializableParameterBindingsDrawer.cs 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. #if UNITY_EDITOR
  3. using System.Collections.Generic;
  4. using UnityEditor;
  5. using UnityEditor.Animations;
  6. using UnityEngine;
  7. using static Animancer.Editor.AnimancerGUI;
  8. namespace Animancer.Editor
  9. {
  10. /// <summary><see cref="PropertyDrawer"/> for <see cref="ControllerState.SerializableParameterBindings"/>.</summary>
  11. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/SerializableParameterBindingsDrawer
  12. [CustomPropertyDrawer(typeof(ControllerState.SerializableParameterBindings), true)]
  13. public class SerializableParameterBindingsDrawer : PropertyDrawer
  14. {
  15. /************************************************************************************************************************/
  16. /// <inheritdoc/>
  17. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
  18. {
  19. if (!property.isExpanded)
  20. return LineHeight;
  21. GetFields(property, out var mode, out var bindings);
  22. var count = bindings.arraySize;
  23. if (count > 0 && mode.boolValue)
  24. count = 1 + Mathf.CeilToInt(count * 0.5f);
  25. return CalculateHeight(count + 3);
  26. }
  27. /************************************************************************************************************************/
  28. /// <inheritdoc/>
  29. public override void OnGUI(Rect area, SerializedProperty property, GUIContent label)
  30. {
  31. area.height = LineHeight;
  32. var isExpanded = EditorGUI.PropertyField(area, property, label, false);
  33. if (!isExpanded)
  34. return;
  35. EditorGUI.indentLevel++;
  36. NextVerticalArea(ref area);
  37. GetFields(property, out var mode, out var bindings);
  38. var parameterList = GetContextParameterList(property);
  39. var bindingCount = bindings.arraySize;
  40. DoModeGUI(ref area, mode, bindingCount, parameterList);
  41. var modeValue = mode.boolValue;
  42. DoBindingCountGUI(ref area, bindings, modeValue, ref bindingCount, parameterList);
  43. DoBindingsGUI(area, bindings, modeValue, bindingCount, parameterList);
  44. EditorGUI.indentLevel--;
  45. }
  46. /************************************************************************************************************************/
  47. private void DoModeGUI(
  48. ref Rect area,
  49. SerializedProperty mode,
  50. int bindingCount,
  51. string parameterList)
  52. {
  53. using var label = PooledGUIContent.Acquire();
  54. if (bindingCount == 0)
  55. {
  56. label.text = "Bind All Parameters";
  57. label.tooltip =
  58. "If enabled, all parameters in the Animator Controller will be bound" +
  59. " to Animancer parameters with the same name and the Bindings array can be left empty." +
  60. parameterList;
  61. }
  62. else
  63. {
  64. label.text = "Rebind Names";
  65. label.tooltip =
  66. "If enabled, the Bindings array will be taken in pairs so that each" +
  67. " Animator Controller parameter can be bound to an Animancer Parameter with different name." +
  68. parameterList;
  69. }
  70. EditorGUI.PropertyField(area, mode, label, false);
  71. NextVerticalArea(ref area);
  72. }
  73. /************************************************************************************************************************/
  74. private void DoBindingCountGUI(
  75. ref Rect area,
  76. SerializedProperty bindings,
  77. bool mode,
  78. ref int bindingCount,
  79. string parameterList)
  80. {
  81. using var label = PooledGUIContent.Acquire(
  82. "Bindings",
  83. "The names of parameters in the Animator Controller to bind to Animancer parameters." +
  84. "\n� Leave this array empty and enable the toggle if you want to bind all parameters." +
  85. parameterList);
  86. var newCount = bindingCount;
  87. if (mode && bindingCount > 0)
  88. newCount /= 2;
  89. newCount = EditorGUI.DelayedIntField(area, label, newCount);
  90. if (newCount < 0)
  91. newCount = 0;
  92. else if (mode && newCount > 0)
  93. newCount *= 2;
  94. if (bindingCount != newCount)
  95. {
  96. bindingCount = newCount;
  97. bindings.arraySize = newCount;
  98. }
  99. NextVerticalArea(ref area);
  100. }
  101. /************************************************************************************************************************/
  102. private void DoBindingsGUI(
  103. Rect area,
  104. SerializedProperty bindings,
  105. bool mode,
  106. int bindingCount,
  107. string parameterList)
  108. {
  109. if (bindingCount <= 0)
  110. return;
  111. using var label = PooledGUIContent.Acquire();
  112. if (mode)
  113. {
  114. var controllerArea = EditorGUI.IndentedRect(area);
  115. controllerArea.xMin -= 1;// Not sure why.
  116. var animancerArea = StealFromRight(ref controllerArea, controllerArea.width * 0.5f, StandardSpacing);
  117. label.text = "Controller";
  118. label.tooltip = "The name of the Animator Controller parameter" + parameterList;
  119. GUI.Label(controllerArea, label);
  120. label.text = "Animancer";
  121. label.tooltip = "The name of the Animancer parameter";
  122. GUI.Label(animancerArea, label);
  123. NextVerticalArea(ref controllerArea);
  124. NextVerticalArea(ref animancerArea);
  125. for (int i = 0; i < bindingCount; i++)
  126. {
  127. DoBindingGUI(ref controllerArea, bindings, i, GUIContent.none);
  128. i++;
  129. DoBindingGUI(ref animancerArea, bindings, i, GUIContent.none);
  130. }
  131. }
  132. else
  133. {
  134. EditorGUI.indentLevel++;
  135. label.tooltip = "";
  136. for (int i = 0; i < bindingCount; i++)
  137. {
  138. label.text = "Binding " + i;
  139. DoBindingGUI(ref area, bindings, i, label);
  140. }
  141. EditorGUI.indentLevel--;
  142. }
  143. }
  144. /************************************************************************************************************************/
  145. private static void DoBindingGUI(ref Rect area, SerializedProperty bindings, int index, GUIContent label)
  146. {
  147. var indentLevel = EditorGUI.indentLevel;
  148. if (string.IsNullOrEmpty(label.text))
  149. EditorGUI.indentLevel = 0;
  150. var binding = bindings.GetArrayElementAtIndex(index);
  151. EditorGUI.PropertyField(area, binding, label);
  152. NextVerticalArea(ref area);
  153. EditorGUI.indentLevel = indentLevel;
  154. }
  155. /************************************************************************************************************************/
  156. private void GetFields(
  157. SerializedProperty root,
  158. out SerializedProperty mode,
  159. out SerializedProperty bindings)
  160. {
  161. mode = root.FindPropertyRelative(ControllerState.SerializableParameterBindings.ModeFieldName);
  162. bindings = root.FindPropertyRelative(ControllerState.SerializableParameterBindings.BindingsFieldName);
  163. }
  164. /************************************************************************************************************************/
  165. private string GetContextParameterList(SerializedProperty property)
  166. {
  167. var path = property.propertyPath;
  168. var lastDot = path.LastIndexOf('.');
  169. if (lastDot < 0)
  170. return null;
  171. path = path[..(lastDot + 1)] + ControllerTransition.ControllerFieldName;
  172. property = property.serializedObject.FindProperty(path);
  173. if (property == null ||
  174. property.objectReferenceValue is not AnimatorController animatorController)
  175. return null;
  176. return GetParameterList(animatorController);
  177. }
  178. private readonly Dictionary<AnimatorController, string>
  179. ControllerToParameterList = new();
  180. private string GetParameterList(AnimatorController animatorController)
  181. {
  182. if (animatorController == null)
  183. return null;
  184. if (ControllerToParameterList.TryGetValue(animatorController, out var parameterList))
  185. return parameterList;
  186. var text = StringBuilderPool.Instance.Acquire();
  187. var parameters = animatorController.parameters;
  188. if (parameters.Length > 0)
  189. {
  190. text.Append("\n\nParameters in ")
  191. .Append(animatorController.name)
  192. .Append(':');
  193. for (int i = 0; i < parameters.Length; i++)
  194. {
  195. var parameter = parameters[i];
  196. text.Append("\n� ")
  197. .Append(parameter.type)
  198. .Append(' ')
  199. .Append(parameter.name);
  200. }
  201. }
  202. parameterList = text.ReleaseToString();
  203. ControllerToParameterList.Add(animatorController, parameterList);
  204. return parameterList;
  205. }
  206. /************************************************************************************************************************/
  207. }
  208. }
  209. #endif