MixerTransition2DAsset.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. using System;
  3. using UnityEngine;
  4. using Object = UnityEngine.Object;
  5. #if UNITY_EDITOR
  6. using UnityEditor;
  7. #endif
  8. namespace Animancer
  9. {
  10. /// <inheritdoc/>
  11. /// https://kybernetik.com.au/animancer/api/Animancer/MixerTransition2DAsset
  12. [CreateAssetMenu(menuName = Strings.MenuPrefix + "Mixer Transition/2D", order = Strings.AssetMenuOrder + 4)]
  13. [HelpURL(Strings.DocsURLs.APIDocumentation + "/" + nameof(MixerTransition2DAsset))]
  14. public class MixerTransition2DAsset : AnimancerTransitionAsset<MixerTransition2D>
  15. {
  16. /// <inheritdoc/>
  17. [Serializable]
  18. public new class UnShared :
  19. UnShared<MixerTransition2DAsset, MixerTransition2D, MixerState<Vector2>>,
  20. MixerState.ITransition2D
  21. { }
  22. }
  23. /// <inheritdoc/>
  24. /// https://kybernetik.com.au/animancer/api/Animancer/MixerTransition2D
  25. [Serializable]
  26. public class MixerTransition2D : MixerTransition<MixerState<Vector2>, Vector2>,
  27. MixerState.ITransition2D, ICopyable<MixerTransition2D>
  28. {
  29. /************************************************************************************************************************/
  30. /// <summary>A type of <see cref="MixerState"/> that can be created by a <see cref="MixerTransition2D"/>.</summary>
  31. public enum MixerType
  32. {
  33. /// <summary><see cref="CartesianMixerState"/></summary>
  34. Cartesian,
  35. /// <summary><see cref="DirectionalMixerState"/></summary>
  36. Directional,
  37. }
  38. [SerializeField]
  39. private MixerType _Type;
  40. /// <summary>[<see cref="SerializeField"/>]
  41. /// The type of <see cref="MixerState"/> that this transition will create.
  42. /// </summary>
  43. public ref MixerType Type => ref _Type;
  44. /************************************************************************************************************************/
  45. /// <summary>
  46. /// Creates and returns a new <see cref="CartesianMixerState"/> or <see cref="DirectionalMixerState"/>
  47. /// depending on the <see cref="Type"/>.
  48. /// </summary>
  49. /// <remarks>
  50. /// Note that using methods like <see cref="AnimancerPlayable.Play(ITransition)"/> will also call
  51. /// <see cref="ITransition.Apply"/>, so if you call this method manually you may want to call that method
  52. /// as well. Or you can just use <see cref="AnimancerUtilities.CreateStateAndApply"/>.
  53. /// <para></para>
  54. /// This method also assigns it as the <see cref="AnimancerTransition{TState}.State"/>.
  55. /// </remarks>
  56. public override MixerState<Vector2> CreateState()
  57. {
  58. switch (_Type)
  59. {
  60. case MixerType.Cartesian: State = new CartesianMixerState(); break;
  61. case MixerType.Directional: State = new DirectionalMixerState(); break;
  62. default: throw new ArgumentOutOfRangeException(nameof(_Type));
  63. }
  64. InitializeState();
  65. return State;
  66. }
  67. /************************************************************************************************************************/
  68. /// <inheritdoc/>
  69. public virtual void CopyFrom(MixerTransition2D copyFrom)
  70. {
  71. CopyFrom((MixerTransition<MixerState<Vector2>, Vector2>)copyFrom);
  72. if (copyFrom == null)
  73. {
  74. _Type = default;
  75. return;
  76. }
  77. _Type = copyFrom._Type;
  78. }
  79. /************************************************************************************************************************/
  80. #region Drawer
  81. #if UNITY_EDITOR
  82. /************************************************************************************************************************/
  83. /// <inheritdoc/>
  84. [CustomPropertyDrawer(typeof(MixerTransition2D), true)]
  85. public class Drawer : MixerTransitionDrawer
  86. {
  87. /************************************************************************************************************************/
  88. /// <summary>
  89. /// Creates a new <see cref="Drawer"/> using the a wider `thresholdWidth` than usual to accomodate
  90. /// both the X and Y values.
  91. /// </summary>
  92. public Drawer() : base(StandardThresholdWidth * 2 + 20) { }
  93. /************************************************************************************************************************/
  94. #region Threshold Calculation Functions
  95. /************************************************************************************************************************/
  96. /// <inheritdoc/>
  97. protected override void AddThresholdFunctionsToMenu(GenericMenu menu)
  98. {
  99. AddCalculateThresholdsFunction(menu, "From Velocity/XY", (state, threshold) =>
  100. {
  101. if (AnimancerUtilities.TryGetAverageVelocity(state, out var velocity))
  102. return new Vector2(velocity.x, velocity.y);
  103. else
  104. return new Vector2(float.NaN, float.NaN);
  105. });
  106. AddCalculateThresholdsFunction(menu, "From Velocity/XZ", (state, threshold) =>
  107. {
  108. if (AnimancerUtilities.TryGetAverageVelocity(state, out var velocity))
  109. return new Vector2(velocity.x, velocity.z);
  110. else
  111. return new Vector2(float.NaN, float.NaN);
  112. });
  113. AddCalculateThresholdsFunctionPerAxis(menu, "From Speed",
  114. (state, threshold) => AnimancerUtilities.TryGetAverageVelocity(state, out var velocity) ? velocity.magnitude : float.NaN);
  115. AddCalculateThresholdsFunctionPerAxis(menu, "From Velocity X",
  116. (state, threshold) => AnimancerUtilities.TryGetAverageVelocity(state, out var velocity) ? velocity.x : float.NaN);
  117. AddCalculateThresholdsFunctionPerAxis(menu, "From Velocity Y",
  118. (state, threshold) => AnimancerUtilities.TryGetAverageVelocity(state, out var velocity) ? velocity.y : float.NaN);
  119. AddCalculateThresholdsFunctionPerAxis(menu, "From Velocity Z",
  120. (state, threshold) => AnimancerUtilities.TryGetAverageVelocity(state, out var velocity) ? velocity.z : float.NaN);
  121. AddCalculateThresholdsFunctionPerAxis(menu, "From Angular Speed (Rad)",
  122. (state, threshold) => AnimancerUtilities.TryGetAverageAngularSpeed(state, out var speed) ? speed : float.NaN);
  123. AddCalculateThresholdsFunctionPerAxis(menu, "From Angular Speed (Deg)",
  124. (state, threshold) => AnimancerUtilities.TryGetAverageAngularSpeed(state, out var speed) ? speed * Mathf.Rad2Deg : float.NaN);
  125. AddPropertyModifierFunction(menu, "Initialize 4 Directions", Initialize4Directions);
  126. AddPropertyModifierFunction(menu, "Initialize 8 Directions", Initialize8Directions);
  127. }
  128. /************************************************************************************************************************/
  129. private void Initialize4Directions(SerializedProperty property)
  130. {
  131. var oldSpeedCount = CurrentSpeeds.arraySize;
  132. CurrentAnimations.arraySize = CurrentThresholds.arraySize = CurrentSpeeds.arraySize = 5;
  133. CurrentThresholds.GetArrayElementAtIndex(0).vector2Value = default;
  134. CurrentThresholds.GetArrayElementAtIndex(1).vector2Value = Vector2.up;
  135. CurrentThresholds.GetArrayElementAtIndex(2).vector2Value = Vector2.right;
  136. CurrentThresholds.GetArrayElementAtIndex(3).vector2Value = Vector2.down;
  137. CurrentThresholds.GetArrayElementAtIndex(4).vector2Value = Vector2.left;
  138. InitializeSpeeds(oldSpeedCount);
  139. var type = property.FindPropertyRelative(nameof(_Type));
  140. type.enumValueIndex = (int)MixerType.Directional;
  141. }
  142. /************************************************************************************************************************/
  143. private void Initialize8Directions(SerializedProperty property)
  144. {
  145. var oldSpeedCount = CurrentSpeeds.arraySize;
  146. CurrentAnimations.arraySize = CurrentThresholds.arraySize = CurrentSpeeds.arraySize = 9;
  147. CurrentThresholds.GetArrayElementAtIndex(0).vector2Value = default;
  148. CurrentThresholds.GetArrayElementAtIndex(1).vector2Value = Vector2.up;
  149. CurrentThresholds.GetArrayElementAtIndex(2).vector2Value = new Vector2(1, 1);
  150. CurrentThresholds.GetArrayElementAtIndex(3).vector2Value = Vector2.right;
  151. CurrentThresholds.GetArrayElementAtIndex(4).vector2Value = new Vector2(1, -1);
  152. CurrentThresholds.GetArrayElementAtIndex(5).vector2Value = Vector2.down;
  153. CurrentThresholds.GetArrayElementAtIndex(6).vector2Value = new Vector2(-1, -1);
  154. CurrentThresholds.GetArrayElementAtIndex(7).vector2Value = Vector2.left;
  155. CurrentThresholds.GetArrayElementAtIndex(8).vector2Value = new Vector2(-1, 1);
  156. InitializeSpeeds(oldSpeedCount);
  157. var type = property.FindPropertyRelative(nameof(_Type));
  158. type.enumValueIndex = (int)MixerType.Directional;
  159. }
  160. /************************************************************************************************************************/
  161. private void AddCalculateThresholdsFunction(GenericMenu menu, string label,
  162. Func<Object, Vector2, Vector2> calculateThreshold)
  163. {
  164. var functionState = CurrentAnimations == null || CurrentThresholds == null
  165. ? Editor.MenuFunctionState.Disabled
  166. : Editor.MenuFunctionState.Normal;
  167. AddPropertyModifierFunction(menu, label, functionState, property =>
  168. {
  169. GatherSubProperties(property);
  170. if (CurrentAnimations == null ||
  171. CurrentThresholds == null)
  172. return;
  173. var count = CurrentAnimations.arraySize;
  174. for (int i = 0; i < count; i++)
  175. {
  176. var state = CurrentAnimations.GetArrayElementAtIndex(i).objectReferenceValue;
  177. if (state == null)
  178. continue;
  179. var threshold = CurrentThresholds.GetArrayElementAtIndex(i);
  180. var value = calculateThreshold(state, threshold.vector2Value);
  181. if (!Editor.AnimancerEditorUtilities.IsNaN(value))
  182. threshold.vector2Value = value;
  183. }
  184. });
  185. }
  186. /************************************************************************************************************************/
  187. private void AddCalculateThresholdsFunctionPerAxis(GenericMenu menu, string label,
  188. Func<Object, float, float> calculateThreshold)
  189. {
  190. AddCalculateThresholdsFunction(menu, "X/" + label, 0, calculateThreshold);
  191. AddCalculateThresholdsFunction(menu, "Y/" + label, 1, calculateThreshold);
  192. }
  193. private void AddCalculateThresholdsFunction(GenericMenu menu, string label, int axis,
  194. Func<Object, float, float> calculateThreshold)
  195. {
  196. AddPropertyModifierFunction(menu, label, (property) =>
  197. {
  198. var count = CurrentAnimations.arraySize;
  199. for (int i = 0; i < count; i++)
  200. {
  201. var state = CurrentAnimations.GetArrayElementAtIndex(i).objectReferenceValue;
  202. if (state == null)
  203. continue;
  204. var threshold = CurrentThresholds.GetArrayElementAtIndex(i);
  205. var value = threshold.vector2Value;
  206. var newValue = calculateThreshold(state, value[axis]);
  207. if (!float.IsNaN(newValue))
  208. value[axis] = newValue;
  209. threshold.vector2Value = value;
  210. }
  211. });
  212. }
  213. /************************************************************************************************************************/
  214. #endregion
  215. /************************************************************************************************************************/
  216. }
  217. /************************************************************************************************************************/
  218. #endif
  219. #endregion
  220. /************************************************************************************************************************/
  221. }
  222. }