AnimationPlayableAsset.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. using System.Collections.Generic;
  2. using UnityEngine.Animations;
  3. using UnityEngine.Playables;
  4. namespace UnityEngine.Timeline
  5. {
  6. /// <summary>
  7. /// A Playable Asset that represents a single AnimationClip clip.
  8. /// </summary>
  9. [System.Serializable, NotKeyable]
  10. public partial class AnimationPlayableAsset : PlayableAsset, ITimelineClipAsset, IPropertyPreview
  11. {
  12. /// <summary>
  13. /// Whether the source AnimationClip loops during playback.
  14. /// </summary>
  15. public enum LoopMode
  16. {
  17. /// <summary>
  18. /// Use the loop time setting from the source AnimationClip.
  19. /// </summary>
  20. [Tooltip("Use the loop time setting from the source AnimationClip.")]
  21. UseSourceAsset = 0,
  22. /// <summary>
  23. /// The source AnimationClip loops during playback.
  24. /// </summary>
  25. [Tooltip("The source AnimationClip loops during playback.")]
  26. On = 1,
  27. /// <summary>
  28. /// The source AnimationClip does not loop during playback.
  29. /// </summary>
  30. [Tooltip("The source AnimationClip does not loop during playback.")]
  31. Off = 2
  32. }
  33. [SerializeField] private AnimationClip m_Clip;
  34. [SerializeField] private Vector3 m_Position = Vector3.zero;
  35. [SerializeField] private Vector3 m_EulerAngles = Vector3.zero;
  36. [SerializeField] private bool m_UseTrackMatchFields = true;
  37. [SerializeField] private MatchTargetFields m_MatchTargetFields = MatchTargetFieldConstants.All;
  38. [SerializeField] private bool m_RemoveStartOffset = true; // set by animation track prior to compilation
  39. [SerializeField] private bool m_ApplyFootIK = true;
  40. [SerializeField] private LoopMode m_Loop = LoopMode.UseSourceAsset;
  41. #if UNITY_EDITOR
  42. private AnimationOffsetPlayable m_AnimationOffsetPlayable;
  43. #endif
  44. /// <summary>
  45. /// The translational offset of the clip
  46. /// </summary>
  47. public Vector3 position
  48. {
  49. get
  50. {
  51. return m_Position;
  52. }
  53. set
  54. {
  55. m_Position = value;
  56. #if UNITY_EDITOR
  57. if (m_AnimationOffsetPlayable.IsValid())
  58. m_AnimationOffsetPlayable.SetPosition(position);
  59. #endif
  60. }
  61. }
  62. /// <summary>
  63. /// The rotational offset of the clip, expressed as a Quaternion
  64. /// </summary>
  65. public Quaternion rotation
  66. {
  67. get
  68. {
  69. return Quaternion.Euler(m_EulerAngles);
  70. }
  71. set
  72. {
  73. m_EulerAngles = value.eulerAngles;
  74. #if UNITY_EDITOR
  75. if (m_AnimationOffsetPlayable.IsValid())
  76. m_AnimationOffsetPlayable.SetRotation(value);
  77. #endif
  78. }
  79. }
  80. /// <summary>
  81. /// The rotational offset of the clip, expressed in Euler angles
  82. /// </summary>
  83. public Vector3 eulerAngles
  84. {
  85. get { return m_EulerAngles; }
  86. set
  87. {
  88. m_EulerAngles = value;
  89. #if UNITY_EDITOR
  90. if (m_AnimationOffsetPlayable.IsValid())
  91. m_AnimationOffsetPlayable.SetRotation(rotation);
  92. #endif
  93. }
  94. }
  95. /// <summary>
  96. /// Specifies whether to use offset matching options as defined by the track.
  97. /// </summary>
  98. public bool useTrackMatchFields
  99. {
  100. get { return m_UseTrackMatchFields; }
  101. set { m_UseTrackMatchFields = value; }
  102. }
  103. /// <summary>
  104. /// Specifies which fields should be matched when aligning offsets.
  105. /// </summary>
  106. public MatchTargetFields matchTargetFields
  107. {
  108. get { return m_MatchTargetFields; }
  109. set { m_MatchTargetFields = value; }
  110. }
  111. /// <summary>
  112. /// Whether to make the animation clip play relative to its first keyframe.
  113. /// </summary>
  114. /// <remarks>
  115. /// This option only applies to animation clips that animate Transform components.
  116. /// </remarks>
  117. public bool removeStartOffset
  118. {
  119. get { return m_RemoveStartOffset; }
  120. set { m_RemoveStartOffset = value; }
  121. }
  122. /// <summary>
  123. /// Enable to apply foot IK to the AnimationClip when the target is humanoid.
  124. /// </summary>
  125. public bool applyFootIK
  126. {
  127. get { return m_ApplyFootIK; }
  128. set { m_ApplyFootIK = value; }
  129. }
  130. /// <summary>
  131. /// Whether the source AnimationClip loops during playback
  132. /// </summary>
  133. public LoopMode loop
  134. {
  135. get { return m_Loop; }
  136. set { m_Loop = value; }
  137. }
  138. internal bool hasRootTransforms
  139. {
  140. get { return m_Clip != null && HasRootTransforms(m_Clip); }
  141. }
  142. // used for legacy 'scene' mode.
  143. internal AppliedOffsetMode appliedOffsetMode { get; set; }
  144. /// <summary>
  145. /// The source animation clip
  146. /// </summary>
  147. public AnimationClip clip
  148. {
  149. get { return m_Clip; }
  150. set
  151. {
  152. if (value != null)
  153. name = "AnimationPlayableAsset of " + value.name;
  154. m_Clip = value;
  155. }
  156. }
  157. /// <summary>
  158. /// Returns the duration required to play the animation clip exactly once
  159. /// </summary>
  160. public override double duration
  161. {
  162. get
  163. {
  164. double length = TimeUtility.GetAnimationClipLength(clip);
  165. if (length < float.Epsilon)
  166. return base.duration;
  167. return length;
  168. }
  169. }
  170. /// <summary>
  171. /// Returns a description of the PlayableOutputs that may be created for this asset.
  172. /// </summary>
  173. public override IEnumerable<PlayableBinding> outputs
  174. {
  175. get { yield return AnimationPlayableBinding.Create(name, this); }
  176. }
  177. /// <summary>
  178. /// Creates the root of a Playable subgraph to play the animation clip.
  179. /// </summary>
  180. /// <param name="graph">PlayableGraph that will own the playable</param>
  181. /// <param name="go">The gameobject that triggered the graph build</param>
  182. /// <returns>The root playable of the subgraph</returns>
  183. public override Playable CreatePlayable(PlayableGraph graph, GameObject go)
  184. {
  185. Playable root = CreatePlayable(graph, m_Clip, position, eulerAngles, removeStartOffset, appliedOffsetMode, applyFootIK, m_Loop);
  186. #if UNITY_EDITOR
  187. m_AnimationOffsetPlayable = AnimationOffsetPlayable.Null;
  188. if (root.IsValid() && root.IsPlayableOfType<AnimationOffsetPlayable>())
  189. {
  190. m_AnimationOffsetPlayable = (AnimationOffsetPlayable)root;
  191. }
  192. LiveLink();
  193. #endif
  194. return root;
  195. }
  196. internal static Playable CreatePlayable(PlayableGraph graph, AnimationClip clip, Vector3 positionOffset, Vector3 eulerOffset, bool removeStartOffset, AppliedOffsetMode mode, bool applyFootIK, LoopMode loop)
  197. {
  198. if (clip == null || clip.legacy)
  199. return Playable.Null;
  200. var clipPlayable = AnimationClipPlayable.Create(graph, clip);
  201. clipPlayable.SetRemoveStartOffset(removeStartOffset);
  202. clipPlayable.SetApplyFootIK(applyFootIK);
  203. clipPlayable.SetOverrideLoopTime(loop != LoopMode.UseSourceAsset);
  204. clipPlayable.SetLoopTime(loop == LoopMode.On);
  205. Playable root = clipPlayable;
  206. if (ShouldApplyScaleRemove(mode))
  207. {
  208. var removeScale = AnimationRemoveScalePlayable.Create(graph, 1);
  209. graph.Connect(root, 0, removeScale, 0);
  210. removeScale.SetInputWeight(0, 1.0f);
  211. root = removeScale;
  212. }
  213. if (ShouldApplyOffset(mode, clip))
  214. {
  215. var offsetPlayable = AnimationOffsetPlayable.Create(graph, positionOffset, Quaternion.Euler(eulerOffset), 1);
  216. graph.Connect(root, 0, offsetPlayable, 0);
  217. offsetPlayable.SetInputWeight(0, 1.0F);
  218. root = offsetPlayable;
  219. }
  220. return root;
  221. }
  222. private static bool ShouldApplyOffset(AppliedOffsetMode mode, AnimationClip clip)
  223. {
  224. if (mode == AppliedOffsetMode.NoRootTransform || mode == AppliedOffsetMode.SceneOffsetLegacy)
  225. return false;
  226. return HasRootTransforms(clip);
  227. }
  228. private static bool ShouldApplyScaleRemove(AppliedOffsetMode mode)
  229. {
  230. return mode == AppliedOffsetMode.SceneOffsetLegacyEditor || mode == AppliedOffsetMode.SceneOffsetLegacy || mode == AppliedOffsetMode.TransformOffsetLegacy;
  231. }
  232. #if UNITY_EDITOR
  233. public void LiveLink()
  234. {
  235. if (m_AnimationOffsetPlayable.IsValid())
  236. {
  237. m_AnimationOffsetPlayable.SetPosition(position);
  238. m_AnimationOffsetPlayable.SetRotation(rotation);
  239. }
  240. }
  241. #endif
  242. /// <summary>
  243. /// Returns the capabilities of TimelineClips that contain a AnimationPlayableAsset
  244. /// </summary>
  245. public ClipCaps clipCaps
  246. {
  247. get
  248. {
  249. var caps = ClipCaps.All;
  250. if (m_Clip == null || (m_Loop == LoopMode.Off) || (m_Loop == LoopMode.UseSourceAsset && !m_Clip.isLooping))
  251. caps &= ~ClipCaps.Looping;
  252. // empty clips don't support clip in. This allows trim operations to simply become
  253. // move operations
  254. if (m_Clip == null || m_Clip.empty)
  255. caps &= ~ClipCaps.ClipIn;
  256. return caps;
  257. }
  258. }
  259. /// <summary>
  260. /// Resets the offsets to default values
  261. /// </summary>
  262. public void ResetOffsets()
  263. {
  264. position = Vector3.zero;
  265. eulerAngles = Vector3.zero;
  266. }
  267. /// <inheritdoc/>
  268. public void GatherProperties(PlayableDirector director, IPropertyCollector driver)
  269. {
  270. driver.AddFromClip(m_Clip);
  271. }
  272. internal static bool HasRootTransforms(AnimationClip clip)
  273. {
  274. if (clip == null || clip.empty)
  275. return false;
  276. return clip.hasRootMotion || clip.hasGenericRootTransform || clip.hasMotionCurves || clip.hasRootCurves;
  277. }
  278. }
  279. }