MixerTransition.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value.
  3. using System;
  4. using UnityEngine;
  5. #if UNITY_EDITOR
  6. using UnityEditor;
  7. using UnityEditorInternal;
  8. #endif
  9. namespace Animancer
  10. {
  11. /// <inheritdoc/>
  12. /// https://kybernetik.com.au/animancer/api/Animancer/MixerTransition_2
  13. [Serializable]
  14. public abstract class MixerTransition<TMixer, TParameter> : ManualMixerTransition<TMixer>
  15. , ICopyable<MixerTransition<TMixer, TParameter>>
  16. where TMixer : MixerState<TParameter>
  17. {
  18. /************************************************************************************************************************/
  19. [SerializeField]
  20. private TParameter[] _Thresholds;
  21. /// <summary>[<see cref="SerializeField"/>]
  22. /// The parameter values at which each of the states are used and blended.
  23. /// </summary>
  24. public ref TParameter[] Thresholds => ref _Thresholds;
  25. /// <summary>The name of the serialized backing field of <see cref="Thresholds"/>.</summary>
  26. public const string ThresholdsField = nameof(_Thresholds);
  27. /************************************************************************************************************************/
  28. [SerializeField]
  29. private TParameter _DefaultParameter;
  30. /// <summary>[<see cref="SerializeField"/>]
  31. /// The initial parameter value to give the mixer when it is first created.
  32. /// </summary>
  33. public ref TParameter DefaultParameter => ref _DefaultParameter;
  34. /// <summary>The name of the serialized backing field of <see cref="DefaultParameter"/>.</summary>
  35. public const string DefaultParameterField = nameof(_DefaultParameter);
  36. /************************************************************************************************************************/
  37. /// <inheritdoc/>
  38. public override void InitializeState()
  39. {
  40. base.InitializeState();
  41. State.SetThresholds(_Thresholds);
  42. State.Parameter = _DefaultParameter;
  43. }
  44. /************************************************************************************************************************/
  45. /// <inheritdoc/>
  46. public virtual void CopyFrom(MixerTransition<TMixer, TParameter> copyFrom)
  47. {
  48. CopyFrom((ManualMixerTransition<TMixer>)copyFrom);
  49. if (copyFrom == null)
  50. {
  51. _DefaultParameter = default;
  52. _Thresholds = default;
  53. return;
  54. }
  55. _DefaultParameter = copyFrom._DefaultParameter;
  56. AnimancerUtilities.CopyExactArray(copyFrom._Thresholds, ref _Thresholds);
  57. }
  58. /************************************************************************************************************************/
  59. }
  60. /************************************************************************************************************************/
  61. #if UNITY_EDITOR
  62. /// <summary>[Editor-Only] Draws the Inspector GUI for a <see cref="Transition{TMixer, TParameter}"/>.</summary>
  63. /// <remarks>
  64. /// Documentation: <see href="https://kybernetik.com.au/animancer/docs/manual/transitions">Transitions</see>
  65. /// and <see href="https://kybernetik.com.au/animancer/docs/manual/blending/mixers">Mixers</see>
  66. /// </remarks>
  67. /// https://kybernetik.com.au/animancer/api/Animancer/MixerTransitionDrawer
  68. ///
  69. public class MixerTransitionDrawer : ManualMixerTransition.Drawer
  70. {
  71. /************************************************************************************************************************/
  72. /// <summary>The number of horizontal pixels the "Threshold" label occupies.</summary>
  73. private readonly float ThresholdWidth;
  74. /************************************************************************************************************************/
  75. private static float _StandardThresholdWidth;
  76. /// <summary>
  77. /// The number of horizontal pixels the word "Threshold" occupies when drawn with the
  78. /// <see cref="EditorStyles.popup"/> style.
  79. /// </summary>
  80. protected static float StandardThresholdWidth
  81. {
  82. get
  83. {
  84. if (_StandardThresholdWidth == 0)
  85. _StandardThresholdWidth = Editor.AnimancerGUI.CalculateWidth(EditorStyles.popup, "Threshold");
  86. return _StandardThresholdWidth;
  87. }
  88. }
  89. /************************************************************************************************************************/
  90. /// <summary>
  91. /// Creates a new <see cref="MixerTransitionDrawer"/> using the default <see cref="StandardThresholdWidth"/>.
  92. /// </summary>
  93. public MixerTransitionDrawer() : this(StandardThresholdWidth) { }
  94. /// <summary>
  95. /// Creates a new <see cref="MixerTransitionDrawer"/> using a custom width for its threshold labels.
  96. /// </summary>
  97. protected MixerTransitionDrawer(float thresholdWidth) => ThresholdWidth = thresholdWidth;
  98. /************************************************************************************************************************/
  99. /// <summary>
  100. /// The serialized <see cref="MixerTransition{TMixer, TParameter}.Thresholds"/> of the
  101. /// <see cref="ManualMixerTransition.Drawer.CurrentProperty"/>.
  102. /// </summary>
  103. protected static SerializedProperty CurrentThresholds { get; private set; }
  104. /************************************************************************************************************************/
  105. /// <inheritdoc/>
  106. protected override void GatherSubProperties(SerializedProperty property)
  107. {
  108. base.GatherSubProperties(property);
  109. CurrentThresholds = property.FindPropertyRelative(MixerTransition2D.ThresholdsField);
  110. if (CurrentAnimations == null ||
  111. CurrentThresholds == null ||
  112. property.hasMultipleDifferentValues)
  113. return;
  114. var count = Math.Max(CurrentAnimations.arraySize, CurrentThresholds.arraySize);
  115. CurrentAnimations.arraySize = count;
  116. CurrentThresholds.arraySize = count;
  117. if (CurrentSpeeds != null &&
  118. CurrentSpeeds.arraySize != 0)
  119. CurrentSpeeds.arraySize = count;
  120. }
  121. /************************************************************************************************************************/
  122. /// <inheritdoc/>
  123. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
  124. {
  125. var height = base.GetPropertyHeight(property, label);
  126. if (property.isExpanded)
  127. {
  128. if (CurrentThresholds != null)
  129. height -= Editor.AnimancerGUI.StandardSpacing + EditorGUI.GetPropertyHeight(CurrentThresholds, label);
  130. }
  131. return height;
  132. }
  133. /************************************************************************************************************************/
  134. /// <inheritdoc/>
  135. protected override void DoChildPropertyGUI(ref Rect area, SerializedProperty rootProperty, SerializedProperty property, GUIContent label)
  136. {
  137. if (property.propertyPath.EndsWith($".{MixerTransition2D.ThresholdsField}"))
  138. return;
  139. base.DoChildPropertyGUI(ref area, rootProperty, property, label);
  140. }
  141. /************************************************************************************************************************/
  142. /// <summary>Splits the specified `area` into separate sections.</summary>
  143. protected void SplitListRect(Rect area, bool isHeader, out Rect animation, out Rect threshold, out Rect speed, out Rect sync)
  144. {
  145. SplitListRect(area, isHeader, out animation, out speed, out sync);
  146. threshold = animation;
  147. var xMin = threshold.xMin = EditorGUIUtility.labelWidth + Editor.AnimancerGUI.IndentSize;
  148. animation.xMax = xMin - Editor.AnimancerGUI.StandardSpacing;
  149. }
  150. /************************************************************************************************************************/
  151. /// <inheritdoc/>
  152. protected override void DoChildListHeaderGUI(Rect area)
  153. {
  154. SplitListRect(area, true, out var animationArea, out var thresholdArea, out var speedArea, out var syncArea);
  155. DoAnimationHeaderGUI(animationArea);
  156. var attribute = Editor.AttributeCache<ThresholdLabelAttribute>.FindAttribute(CurrentThresholds);
  157. var text = attribute != null ? attribute.Label : "Threshold";
  158. using (ObjectPool.Disposable.AcquireContent(out var label, text,
  159. "The parameter values at which each child state will be fully active"))
  160. DoHeaderDropdownGUI(thresholdArea, CurrentThresholds, label, AddThresholdFunctionsToMenu);
  161. DoSpeedHeaderGUI(speedArea);
  162. DoSyncHeaderGUI(syncArea);
  163. }
  164. /************************************************************************************************************************/
  165. /// <inheritdoc/>
  166. protected override void DoElementGUI(Rect area, int index,
  167. SerializedProperty clip, SerializedProperty speed)
  168. {
  169. SplitListRect(area, false, out var animationArea, out var thresholdArea, out var speedArea, out var syncArea);
  170. DoElementGUI(animationArea, speedArea, syncArea, index, clip, speed);
  171. DoThresholdGUI(thresholdArea, index);
  172. }
  173. /************************************************************************************************************************/
  174. /// <summary>Draws the GUI of the threshold at the specified `index`.</summary>
  175. protected virtual void DoThresholdGUI(Rect area, int index)
  176. {
  177. var threshold = CurrentThresholds.GetArrayElementAtIndex(index);
  178. EditorGUI.PropertyField(area, threshold, GUIContent.none);
  179. }
  180. /************************************************************************************************************************/
  181. /// <inheritdoc/>
  182. protected override void OnAddElement(int index)
  183. {
  184. base.OnAddElement(index);
  185. if (CurrentThresholds.arraySize > 0)
  186. CurrentThresholds.InsertArrayElementAtIndex(index);
  187. }
  188. /************************************************************************************************************************/
  189. /// <inheritdoc/>
  190. protected override void OnRemoveElement(ReorderableList list)
  191. {
  192. base.OnRemoveElement(list);
  193. Editor.Serialization.RemoveArrayElement(CurrentThresholds, list.index);
  194. }
  195. /************************************************************************************************************************/
  196. /// <inheritdoc/>
  197. protected override void ResizeList(int size)
  198. {
  199. base.ResizeList(size);
  200. CurrentThresholds.arraySize = size;
  201. }
  202. /************************************************************************************************************************/
  203. /// <inheritdoc/>
  204. protected override void OnReorderList(ReorderableList list, int oldIndex, int newIndex)
  205. {
  206. base.OnReorderList(list, oldIndex, newIndex);
  207. CurrentThresholds.MoveArrayElement(oldIndex, newIndex);
  208. }
  209. /************************************************************************************************************************/
  210. /// <summary>Adds functions to the `menu` relating to the thresholds.</summary>
  211. protected virtual void AddThresholdFunctionsToMenu(GenericMenu menu) { }
  212. /************************************************************************************************************************/
  213. }
  214. #endif
  215. }