ControllerTransitionAsset.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. using System;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEngine.Playables;
  6. using Object = UnityEngine.Object;
  7. using Animancer.Units;
  8. #if UNITY_EDITOR
  9. using UnityEditor;
  10. using UnityEditor.Animations;
  11. #endif
  12. namespace Animancer
  13. {
  14. /// <inheritdoc/>
  15. /// https://kybernetik.com.au/animancer/api/Animancer/ControllerTransitionAsset
  16. [CreateAssetMenu(menuName = Strings.MenuPrefix + "Controller Transition/Base", order = Strings.AssetMenuOrder + 5)]
  17. [HelpURL(Strings.DocsURLs.APIDocumentation + "/" + nameof(ControllerTransitionAsset))]
  18. public class ControllerTransitionAsset : AnimancerTransitionAsset<ControllerTransition>
  19. {
  20. /// <inheritdoc/>
  21. [Serializable]
  22. public new class UnShared :
  23. UnShared<ControllerTransitionAsset, ControllerTransition, ControllerState>,
  24. ControllerState.ITransition
  25. { }
  26. }
  27. /************************************************************************************************************************/
  28. /// <inheritdoc/>
  29. /// https://kybernetik.com.au/animancer/api/Animancer/ControllerTransition_1
  30. [Serializable]
  31. public abstract class ControllerTransition<TState> : AnimancerTransition<TState>,
  32. IAnimationClipCollection, ICopyable<ControllerTransition<TState>>
  33. where TState : ControllerState
  34. {
  35. /************************************************************************************************************************/
  36. [SerializeField]
  37. private RuntimeAnimatorController _Controller;
  38. /// <summary>[<see cref="SerializeField"/>]
  39. /// The <see cref="ControllerState.Controller"/> that will be used for the created state.
  40. /// </summary>
  41. public ref RuntimeAnimatorController Controller => ref _Controller;
  42. /// <inheritdoc/>
  43. public override Object MainObject => _Controller;
  44. #if UNITY_EDITOR
  45. /// <summary>[Editor-Only] The name of the serialized backing field of <see cref="Controller"/>.</summary>
  46. public const string ControllerFieldName = nameof(_Controller);
  47. #endif
  48. /************************************************************************************************************************/
  49. [SerializeField]
  50. [Tooltip("Determines what each layer does when " +
  51. nameof(ControllerState) + "." + nameof(ControllerState.Stop) + " is called." +
  52. "\n• If empty, all layers will reset to their default state." +
  53. "\n• If this array is smaller than the layer count, any additional layers will use the last value in this array.")]
  54. private ControllerState.ActionOnStop[] _ActionsOnStop;
  55. /// <summary>[<see cref="SerializeField"/>]
  56. /// Determines what each layer does when <see cref="ControllerState.Stop"/> is called.
  57. /// </summary>
  58. /// <remarks>
  59. /// If empty, all layers will reset to their <see cref="ControllerState.ActionOnStop.DefaultState"/>.
  60. /// <para></para>
  61. /// If this array is smaller than the
  62. /// <see cref="UnityEngine.Animations.AnimatorControllerPlayable.GetLayerCount"/>, any additional
  63. /// layers will use the last value in this array.
  64. /// </remarks>
  65. public ref ControllerState.ActionOnStop[] ActionsOnStop => ref _ActionsOnStop;
  66. /************************************************************************************************************************/
  67. /// <inheritdoc/>
  68. public override float MaximumDuration
  69. {
  70. get
  71. {
  72. if (_Controller == null)
  73. return 0;
  74. var duration = 0f;
  75. var clips = _Controller.animationClips;
  76. for (int i = 0; i < clips.Length; i++)
  77. {
  78. var length = clips[i].length;
  79. if (duration < length)
  80. duration = length;
  81. }
  82. return duration;
  83. }
  84. }
  85. /************************************************************************************************************************/
  86. /// <inheritdoc/>
  87. public override bool IsValid => _Controller != null;
  88. /************************************************************************************************************************/
  89. /// <summary>Returns the <see cref="Controller"/>.</summary>
  90. public static implicit operator RuntimeAnimatorController(ControllerTransition<TState> transition)
  91. => transition?._Controller;
  92. /************************************************************************************************************************/
  93. /// <inheritdoc/>
  94. public override void Apply(AnimancerState state)
  95. {
  96. if (state is ControllerState controllerState)
  97. controllerState.ActionsOnStop = _ActionsOnStop;
  98. base.Apply(state);
  99. }
  100. /************************************************************************************************************************/
  101. /// <summary>Adds all clips in the <see cref="Controller"/> to the collection.</summary>
  102. void IAnimationClipCollection.GatherAnimationClips(ICollection<AnimationClip> clips)
  103. {
  104. if (_Controller != null)
  105. clips.Gather(_Controller.animationClips);
  106. }
  107. /************************************************************************************************************************/
  108. /// <inheritdoc/>
  109. public virtual void CopyFrom(ControllerTransition<TState> copyFrom)
  110. {
  111. CopyFrom((AnimancerTransition<TState>)copyFrom);
  112. if (copyFrom == null)
  113. {
  114. _Controller = default;
  115. _ActionsOnStop = Array.Empty<ControllerState.ActionOnStop>();
  116. return;
  117. }
  118. _Controller = copyFrom._Controller;
  119. _ActionsOnStop = copyFrom._ActionsOnStop;
  120. }
  121. /************************************************************************************************************************/
  122. }
  123. /************************************************************************************************************************/
  124. /// <inheritdoc/>
  125. /// https://kybernetik.com.au/animancer/api/Animancer/ControllerTransition
  126. [Serializable]
  127. public class ControllerTransition : ControllerTransition<ControllerState>,
  128. ControllerState.ITransition, ICopyable<ControllerTransition>
  129. {
  130. /************************************************************************************************************************/
  131. /// <inheritdoc/>
  132. public override ControllerState CreateState()
  133. {
  134. #if UNITY_ASSERTIONS
  135. if (Controller == null)
  136. throw new ArgumentException(
  137. $"Unable to create {nameof(ControllerState)} because the" +
  138. $" {nameof(ControllerTransition)}.{nameof(Controller)} is null.");
  139. #endif
  140. return State = new ControllerState(Controller, ActionsOnStop);
  141. }
  142. /************************************************************************************************************************/
  143. /// <summary>Creates a new <see cref="ControllerTransition"/>.</summary>
  144. public ControllerTransition() { }
  145. /// <summary>Creates a new <see cref="ControllerTransition"/> with the specified Animator Controller.</summary>
  146. public ControllerTransition(RuntimeAnimatorController controller) => Controller = controller;
  147. /************************************************************************************************************************/
  148. /// <summary>Creates a new <see cref="ControllerTransition"/> with the specified Animator Controller.</summary>
  149. public static implicit operator ControllerTransition(RuntimeAnimatorController controller)
  150. => new ControllerTransition(controller);
  151. /************************************************************************************************************************/
  152. /// <inheritdoc/>
  153. public virtual void CopyFrom(ControllerTransition copyFrom)
  154. {
  155. CopyFrom((ControllerTransition<ControllerState>)copyFrom);
  156. }
  157. /************************************************************************************************************************/
  158. #region Drawer
  159. #if UNITY_EDITOR
  160. /************************************************************************************************************************/
  161. /// <inheritdoc/>
  162. [CustomPropertyDrawer(typeof(ControllerTransition<>), true)]
  163. [CustomPropertyDrawer(typeof(ControllerTransition), true)]
  164. public class Drawer : Editor.TransitionDrawer
  165. {
  166. /************************************************************************************************************************/
  167. private readonly string[] Parameters;
  168. private readonly string[] ParameterPropertySuffixes;
  169. /************************************************************************************************************************/
  170. /// <summary>Creates a new <see cref="Drawer"/> without any parameters.</summary>
  171. public Drawer() : base(ControllerFieldName) { }
  172. /// <summary>Creates a new <see cref="Drawer"/> and sets the <see cref="Parameters"/>.</summary>
  173. public Drawer(params string[] parameters) : base(ControllerFieldName)
  174. {
  175. Parameters = parameters;
  176. if (parameters == null)
  177. return;
  178. ParameterPropertySuffixes = new string[parameters.Length];
  179. for (int i = 0; i < ParameterPropertySuffixes.Length; i++)
  180. {
  181. ParameterPropertySuffixes[i] = "." + parameters[i];
  182. }
  183. }
  184. /************************************************************************************************************************/
  185. /// <inheritdoc/>
  186. protected override void DoChildPropertyGUI(
  187. ref Rect area,
  188. SerializedProperty rootProperty,
  189. SerializedProperty property,
  190. GUIContent label)
  191. {
  192. var path = property.propertyPath;
  193. if (ParameterPropertySuffixes != null)
  194. {
  195. var controllerProperty = rootProperty.FindPropertyRelative(MainPropertyName);
  196. if (controllerProperty.objectReferenceValue is AnimatorController controller)
  197. {
  198. for (int i = 0; i < ParameterPropertySuffixes.Length; i++)
  199. {
  200. if (path.EndsWith(ParameterPropertySuffixes[i]))
  201. {
  202. area.height = Editor.AnimancerGUI.LineHeight;
  203. DoParameterGUI(area, controller, property);
  204. return;
  205. }
  206. }
  207. }
  208. }
  209. EditorGUI.BeginChangeCheck();
  210. base.DoChildPropertyGUI(ref area, rootProperty, property, label);
  211. // When the controller changes, validate all parameters.
  212. if (EditorGUI.EndChangeCheck() &&
  213. Parameters != null &&
  214. path.EndsWith(MainPropertyPathSuffix))
  215. {
  216. if (property.objectReferenceValue is AnimatorController controller)
  217. {
  218. for (int i = 0; i < Parameters.Length; i++)
  219. {
  220. property = rootProperty.FindPropertyRelative(Parameters[i]);
  221. var parameterName = property.stringValue;
  222. // If a parameter is missing, assign it to the first float parameter.
  223. if (!HasFloatParameter(controller, parameterName))
  224. {
  225. parameterName = GetFirstFloatParameterName(controller);
  226. if (!string.IsNullOrEmpty(parameterName))
  227. property.stringValue = parameterName;
  228. }
  229. }
  230. }
  231. }
  232. }
  233. /************************************************************************************************************************/
  234. /// <summary>Draws a dropdown menu to select the name of a parameter in the `controller`.</summary>
  235. protected void DoParameterGUI(Rect area, AnimatorController controller, SerializedProperty property)
  236. {
  237. var parameterName = property.stringValue;
  238. var parameters = controller.parameters;
  239. using (ObjectPool.Disposable.AcquireContent(out var label, property))
  240. {
  241. label = EditorGUI.BeginProperty(area, label, property);
  242. var xMax = area.xMax;
  243. area.width = EditorGUIUtility.labelWidth;
  244. EditorGUI.PrefixLabel(area, label);
  245. area.x += area.width;
  246. area.xMax = xMax;
  247. }
  248. var color = GUI.color;
  249. if (!HasFloatParameter(controller, parameterName))
  250. GUI.color = Editor.AnimancerGUI.ErrorFieldColor;
  251. using (ObjectPool.Disposable.AcquireContent(out var label, parameterName))
  252. {
  253. if (EditorGUI.DropdownButton(area, label, FocusType.Passive))
  254. {
  255. property = property.Copy();
  256. var menu = new GenericMenu();
  257. for (int i = 0; i < parameters.Length; i++)
  258. {
  259. var parameter = parameters[i];
  260. Editor.Serialization.AddPropertyModifierFunction(menu, property, parameter.name,
  261. parameter.type == AnimatorControllerParameterType.Float,
  262. (targetProperty) =>
  263. {
  264. targetProperty.stringValue = parameter.name;
  265. });
  266. }
  267. if (menu.GetItemCount() == 0)
  268. menu.AddDisabledItem(new GUIContent("No Parameters"));
  269. menu.ShowAsContext();
  270. }
  271. }
  272. GUI.color = color;
  273. EditorGUI.EndProperty();
  274. }
  275. /************************************************************************************************************************/
  276. private static bool HasFloatParameter(AnimatorController controller, string name)
  277. {
  278. if (string.IsNullOrEmpty(name))
  279. return false;
  280. var parameters = controller.parameters;
  281. for (int i = 0; i < parameters.Length; i++)
  282. {
  283. var parameter = parameters[i];
  284. if (parameter.type == AnimatorControllerParameterType.Float &&
  285. parameter.name == name)
  286. {
  287. return true;
  288. }
  289. }
  290. return false;
  291. }
  292. /************************************************************************************************************************/
  293. private static string GetFirstFloatParameterName(AnimatorController controller)
  294. {
  295. var parameters = controller.parameters;
  296. for (int i = 0; i < parameters.Length; i++)
  297. {
  298. var parameter = parameters[i];
  299. if (parameter.type == AnimatorControllerParameterType.Float)
  300. {
  301. return parameter.name;
  302. }
  303. }
  304. return "";
  305. }
  306. /************************************************************************************************************************/
  307. }
  308. /************************************************************************************************************************/
  309. #endif
  310. #endregion
  311. /************************************************************************************************************************/
  312. }
  313. }