PlayableAssetState.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Text;
  5. using UnityEngine;
  6. using UnityEngine.Animations;
  7. using UnityEngine.Audio;
  8. using UnityEngine.Playables;
  9. using Object = UnityEngine.Object;
  10. namespace Animancer
  11. {
  12. /// <summary>[Pro-Only] An <see cref="AnimancerState"/> which plays a <see cref="PlayableAsset"/>.</summary>
  13. /// <remarks>
  14. /// Documentation: <see href="https://kybernetik.com.au/animancer/docs/manual/timeline">Timeline</see>
  15. /// </remarks>
  16. /// https://kybernetik.com.au/animancer/api/Animancer/PlayableAssetState
  17. ///
  18. public class PlayableAssetState : AnimancerState
  19. {
  20. /************************************************************************************************************************/
  21. /// <summary>An <see cref="ITransition{TState}"/> that creates a <see cref="PlayableAssetState"/>.</summary>
  22. public interface ITransition : ITransition<PlayableAssetState> { }
  23. /************************************************************************************************************************/
  24. #region Fields and Properties
  25. /************************************************************************************************************************/
  26. /// <summary>The <see cref="PlayableAsset"/> which this state plays.</summary>
  27. private PlayableAsset _Asset;
  28. /// <summary>The <see cref="PlayableAsset"/> which this state plays.</summary>
  29. public PlayableAsset Asset
  30. {
  31. get => _Asset;
  32. set => ChangeMainObject(ref _Asset, value);
  33. }
  34. /// <summary>The <see cref="PlayableAsset"/> which this state plays.</summary>
  35. public override Object MainObject
  36. {
  37. get => _Asset;
  38. set => _Asset = (PlayableAsset)value;
  39. }
  40. /************************************************************************************************************************/
  41. private float _Length;
  42. /// <summary>The <see cref="PlayableAsset.duration"/>.</summary>
  43. public override float Length => _Length;
  44. /************************************************************************************************************************/
  45. /// <inheritdoc/>
  46. protected override void OnSetIsPlaying()
  47. {
  48. var inputCount = _Playable.GetInputCount();
  49. for (int i = 0; i < inputCount; i++)
  50. {
  51. var playable = _Playable.GetInput(i);
  52. if (!playable.IsValid())
  53. continue;
  54. if (IsPlaying)
  55. playable.Play();
  56. else
  57. playable.Pause();
  58. }
  59. }
  60. /************************************************************************************************************************/
  61. /// <summary>IK cannot be dynamically enabled on a <see cref="PlayableAssetState"/>.</summary>
  62. public override void CopyIKFlags(AnimancerNode node) { }
  63. /************************************************************************************************************************/
  64. /// <summary>IK cannot be dynamically enabled on a <see cref="PlayableAssetState"/>.</summary>
  65. public override bool ApplyAnimatorIK
  66. {
  67. get => false;
  68. set
  69. {
  70. #if UNITY_ASSERTIONS
  71. if (value)
  72. OptionalWarning.UnsupportedIK.Log(
  73. $"IK cannot be dynamically enabled on a {nameof(PlayableAssetState)}.", Root?.Component);
  74. #endif
  75. }
  76. }
  77. /************************************************************************************************************************/
  78. /// <summary>IK cannot be dynamically enabled on a <see cref="PlayableAssetState"/>.</summary>
  79. public override bool ApplyFootIK
  80. {
  81. get => false;
  82. set
  83. {
  84. #if UNITY_ASSERTIONS
  85. if (value)
  86. OptionalWarning.UnsupportedIK.Log(
  87. $"IK cannot be dynamically enabled on a {nameof(PlayableAssetState)}.", Root?.Component);
  88. #endif
  89. }
  90. }
  91. /************************************************************************************************************************/
  92. #endregion
  93. /************************************************************************************************************************/
  94. #region Methods
  95. /************************************************************************************************************************/
  96. /// <summary>Creates a new <see cref="PlayableAssetState"/> to play the `asset`.</summary>
  97. /// <exception cref="ArgumentNullException">The `asset` is null.</exception>
  98. public PlayableAssetState(PlayableAsset asset)
  99. {
  100. if (asset == null)
  101. throw new ArgumentNullException(nameof(asset));
  102. _Asset = asset;
  103. }
  104. /************************************************************************************************************************/
  105. /// <inheritdoc/>
  106. protected override void CreatePlayable(out Playable playable)
  107. {
  108. playable = _Asset.CreatePlayable(Root._Graph, Root.Component.gameObject);
  109. playable.SetDuration(9223372.03685477);// https://github.com/KybernetikGames/animancer/issues/111
  110. _Length = (float)_Asset.duration;
  111. if (!_HasInitializedBindings)
  112. InitializeBindings();
  113. }
  114. /************************************************************************************************************************/
  115. private IList<Object> _Bindings;
  116. private bool _HasInitializedBindings;
  117. /************************************************************************************************************************/
  118. /// <summary>The objects controlled by each track in the asset.</summary>
  119. public IList<Object> Bindings
  120. {
  121. get => _Bindings;
  122. set
  123. {
  124. _Bindings = value;
  125. InitializeBindings();
  126. }
  127. }
  128. /************************************************************************************************************************/
  129. /// <summary>Sets the <see cref="Bindings"/>.</summary>
  130. public void SetBindings(params Object[] bindings)
  131. {
  132. Bindings = bindings;
  133. }
  134. /************************************************************************************************************************/
  135. private void InitializeBindings()
  136. {
  137. if (Root == null)
  138. return;
  139. _HasInitializedBindings = true;
  140. Validate.AssertPlayable(this);
  141. var graph = Root._Graph;
  142. var bindableIndex = 0;
  143. var bindableCount = _Bindings != null ? _Bindings.Count : 0;
  144. foreach (var binding in _Asset.outputs)
  145. {
  146. GetBindingDetails(binding, out var trackName, out var trackType, out var isMarkers);
  147. var bindable = bindableIndex < bindableCount ? _Bindings[bindableIndex] : null;
  148. #if UNITY_ASSERTIONS
  149. if (!isMarkers &&
  150. trackType != null &&
  151. bindable != null &&
  152. !trackType.IsAssignableFrom(bindable.GetType()))
  153. {
  154. Debug.LogError(
  155. $"Binding Type Mismatch: bindings[{bindableIndex}] is '{bindable}'" +
  156. $" but should be a {trackType.FullName} for {trackName}",
  157. Root.Component as Object);
  158. bindableIndex++;
  159. continue;
  160. }
  161. #endif
  162. var playable = _Playable.GetInput(bindableIndex);
  163. if (trackType == typeof(Animator))// AnimationTrack.
  164. {
  165. if (bindable != null)
  166. {
  167. #if UNITY_ASSERTIONS
  168. if (bindable == Root.Component?.Animator)
  169. Debug.LogError(
  170. $"{nameof(PlayableAsset)} tracks should not be bound to the same {nameof(Animator)} as" +
  171. $" Animancer. Leaving the binding of the first Animation Track empty will automatically" +
  172. $" apply its animation to the object being controlled by Animancer.",
  173. Root.Component as Object);
  174. #endif
  175. var playableOutput = AnimationPlayableOutput.Create(graph, trackName, (Animator)bindable);
  176. playableOutput.SetReferenceObject(binding.sourceObject);
  177. playableOutput.SetSourcePlayable(playable);
  178. playableOutput.SetWeight(1);
  179. }
  180. }
  181. else if (trackType == typeof(AudioSource))// AudioTrack.
  182. {
  183. if (bindable != null)
  184. {
  185. var playableOutput = AudioPlayableOutput.Create(graph, trackName, (AudioSource)bindable);
  186. playableOutput.SetReferenceObject(binding.sourceObject);
  187. playableOutput.SetSourcePlayable(playable);
  188. playableOutput.SetWeight(1);
  189. }
  190. }
  191. else if (isMarkers)// Markers.
  192. {
  193. var animancer = Root.Component as Component;
  194. var playableOutput = ScriptPlayableOutput.Create(graph, trackName);
  195. playableOutput.SetReferenceObject(binding.sourceObject);
  196. playableOutput.SetSourcePlayable(playable);
  197. playableOutput.SetWeight(1);
  198. playableOutput.SetUserData(animancer);
  199. var receivers = ObjectPool.AcquireList<INotificationReceiver>();
  200. animancer.GetComponents(receivers);
  201. for (int i = 0; i < receivers.Count; i++)
  202. playableOutput.AddNotificationReceiver(receivers[i]);
  203. ObjectPool.Release(receivers);
  204. continue;// Don't increment the bindingIndex.
  205. }
  206. else// ActivationTrack, ControlTrack, PlayableTrack, SignalTrack.
  207. {
  208. var playableOutput = ScriptPlayableOutput.Create(graph, trackName);
  209. playableOutput.SetReferenceObject(binding.sourceObject);
  210. playableOutput.SetSourcePlayable(playable);
  211. playableOutput.SetWeight(1);
  212. playableOutput.SetUserData(bindable);
  213. if (bindable is INotificationReceiver receiver)
  214. playableOutput.AddNotificationReceiver(receiver);
  215. }
  216. bindableIndex++;
  217. }
  218. }
  219. /************************************************************************************************************************/
  220. /// <summary>Should the `binding` be skipped when determining how to map the <see cref="Bindings"/>?</summary>
  221. public static void GetBindingDetails(PlayableBinding binding, out string name, out Type type, out bool isMarkers)
  222. {
  223. name = binding.streamName;
  224. type = binding.outputTargetType;
  225. isMarkers = type == typeof(GameObject) && name == "Markers";
  226. }
  227. /************************************************************************************************************************/
  228. /// <inheritdoc/>
  229. public override void Destroy()
  230. {
  231. _Asset = null;
  232. base.Destroy();
  233. }
  234. /************************************************************************************************************************/
  235. /// <inheritdoc/>
  236. protected override void AppendDetails(StringBuilder text, string separator)
  237. {
  238. base.AppendDetails(text, separator);
  239. text.Append(separator).Append($"{nameof(Bindings)}: ");
  240. int count;
  241. if (_Bindings == null)
  242. {
  243. text.Append("Null");
  244. count = 0;
  245. }
  246. else
  247. {
  248. count = _Bindings.Count;
  249. text.Append('[')
  250. .Append(count)
  251. .Append(']');
  252. }
  253. text.Append(_HasInitializedBindings ? " (Initialized)" : " (Not Initialized)");
  254. for (int i = 0; i < count; i++)
  255. {
  256. text.Append(separator)
  257. .Append($"{nameof(Bindings)}[")
  258. .Append(i)
  259. .Append("] = ")
  260. .Append(AnimancerUtilities.ToStringOrNull(_Bindings[i]));
  261. }
  262. }
  263. /************************************************************************************************************************/
  264. #endregion
  265. /************************************************************************************************************************/
  266. }
  267. }