PlayableAssetState.cs 14 KB

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