326 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			326 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System.Collections.Generic;
 | |
| using UnityEngine.Animations;
 | |
| using UnityEngine.Playables;
 | |
| 
 | |
| namespace UnityEngine.Timeline
 | |
| {
 | |
|     /// <summary>
 | |
|     /// A Playable Asset that represents a single AnimationClip clip.
 | |
|     /// </summary>
 | |
|     [System.Serializable, NotKeyable]
 | |
|     public partial class AnimationPlayableAsset : PlayableAsset, ITimelineClipAsset, IPropertyPreview
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// Whether the source AnimationClip loops during playback.
 | |
|         /// </summary>
 | |
|         public enum LoopMode
 | |
|         {
 | |
|             /// <summary>
 | |
|             /// Use the loop time setting from the source AnimationClip.
 | |
|             /// </summary>
 | |
|             [Tooltip("Use the loop time setting from the source AnimationClip.")]
 | |
|             UseSourceAsset = 0,
 | |
| 
 | |
|             /// <summary>
 | |
|             /// The source AnimationClip loops during playback.
 | |
|             /// </summary>
 | |
|             [Tooltip("The source AnimationClip loops during playback.")]
 | |
|             On = 1,
 | |
| 
 | |
|             /// <summary>
 | |
|             /// The source AnimationClip does not loop during playback.
 | |
|             /// </summary>
 | |
|             [Tooltip("The source AnimationClip does not loop during playback.")]
 | |
|             Off = 2
 | |
|         }
 | |
| 
 | |
| 
 | |
|         [SerializeField] private AnimationClip m_Clip;
 | |
|         [SerializeField] private Vector3 m_Position = Vector3.zero;
 | |
|         [SerializeField] private Vector3 m_EulerAngles = Vector3.zero;
 | |
|         [SerializeField] private bool m_UseTrackMatchFields = true;
 | |
|         [SerializeField] private MatchTargetFields m_MatchTargetFields = MatchTargetFieldConstants.All;
 | |
|         [SerializeField] private bool m_RemoveStartOffset = true; // set by animation track prior to compilation
 | |
|         [SerializeField] private bool m_ApplyFootIK = true;
 | |
|         [SerializeField] private LoopMode m_Loop = LoopMode.UseSourceAsset;
 | |
| 
 | |
| 
 | |
| #if UNITY_EDITOR
 | |
|         private AnimationOffsetPlayable m_AnimationOffsetPlayable;
 | |
| #endif
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The translational offset of the clip
 | |
|         /// </summary>
 | |
|         public Vector3 position
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 return m_Position;
 | |
|             }
 | |
|             set
 | |
|             {
 | |
|                 m_Position = value;
 | |
| #if UNITY_EDITOR
 | |
|                 if (m_AnimationOffsetPlayable.IsValid())
 | |
|                     m_AnimationOffsetPlayable.SetPosition(position);
 | |
| #endif
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The rotational offset of the clip, expressed as a Quaternion
 | |
|         /// </summary>
 | |
|         public Quaternion rotation
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 return Quaternion.Euler(m_EulerAngles);
 | |
|             }
 | |
| 
 | |
|             set
 | |
|             {
 | |
|                 m_EulerAngles = value.eulerAngles;
 | |
| #if UNITY_EDITOR
 | |
|                 if (m_AnimationOffsetPlayable.IsValid())
 | |
|                     m_AnimationOffsetPlayable.SetRotation(value);
 | |
| #endif
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The rotational offset of the clip, expressed in Euler angles
 | |
|         /// </summary>
 | |
|         public Vector3 eulerAngles
 | |
|         {
 | |
|             get { return m_EulerAngles; }
 | |
|             set
 | |
|             {
 | |
|                 m_EulerAngles = value;
 | |
| #if UNITY_EDITOR
 | |
|                 if (m_AnimationOffsetPlayable.IsValid())
 | |
|                     m_AnimationOffsetPlayable.SetRotation(rotation);
 | |
| #endif
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Specifies whether to use offset matching options as defined by the track.
 | |
|         /// </summary>
 | |
|         public bool useTrackMatchFields
 | |
|         {
 | |
|             get { return m_UseTrackMatchFields; }
 | |
|             set { m_UseTrackMatchFields = value; }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Specifies which fields should be matched when aligning offsets.
 | |
|         /// </summary>
 | |
|         public MatchTargetFields matchTargetFields
 | |
|         {
 | |
|             get { return m_MatchTargetFields; }
 | |
|             set { m_MatchTargetFields = value; }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Whether to make the animation clip play relative to its first keyframe.
 | |
|         /// </summary>
 | |
|         /// <remarks>
 | |
|         /// This option only applies to animation clips that animate Transform components.
 | |
|         /// </remarks>
 | |
|         public bool removeStartOffset
 | |
|         {
 | |
|             get { return m_RemoveStartOffset; }
 | |
|             set { m_RemoveStartOffset = value; }
 | |
|         }
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Enable to apply foot IK to the AnimationClip when the target is humanoid.
 | |
|         /// </summary>
 | |
|         public bool applyFootIK
 | |
|         {
 | |
|             get { return m_ApplyFootIK; }
 | |
|             set { m_ApplyFootIK = value; }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Whether the source AnimationClip loops during playback
 | |
|         /// </summary>
 | |
|         public LoopMode loop
 | |
|         {
 | |
|             get { return m_Loop; }
 | |
|             set { m_Loop = value; }
 | |
|         }
 | |
| 
 | |
| 
 | |
|         internal bool hasRootTransforms
 | |
|         {
 | |
|             get { return m_Clip != null && HasRootTransforms(m_Clip); }
 | |
|         }
 | |
| 
 | |
|         // used for legacy 'scene' mode.
 | |
|         internal AppliedOffsetMode appliedOffsetMode { get; set; }
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// The source animation clip
 | |
|         /// </summary>
 | |
|         public AnimationClip clip
 | |
|         {
 | |
|             get { return m_Clip; }
 | |
|             set
 | |
|             {
 | |
|                 if (value != null)
 | |
|                     name = "AnimationPlayableAsset of " + value.name;
 | |
|                 m_Clip = value;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns the duration required to play the animation clip exactly once
 | |
|         /// </summary>
 | |
|         public override double duration
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 double length = TimeUtility.GetAnimationClipLength(clip);
 | |
|                 if (length < float.Epsilon)
 | |
|                     return base.duration;
 | |
|                 return length;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns a description of the PlayableOutputs that may be created for this asset.
 | |
|         /// </summary>
 | |
|         public override IEnumerable<PlayableBinding> outputs
 | |
|         {
 | |
|             get { yield return AnimationPlayableBinding.Create(name, this); }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Creates the root of a Playable subgraph to play the animation clip.
 | |
|         /// </summary>
 | |
|         /// <param name="graph">PlayableGraph that will own the playable</param>
 | |
|         /// <param name="go">The gameobject that triggered the graph build</param>
 | |
|         /// <returns>The root playable of the subgraph</returns>
 | |
|         public override Playable CreatePlayable(PlayableGraph graph, GameObject go)
 | |
|         {
 | |
|             Playable root = CreatePlayable(graph, m_Clip, position, eulerAngles, removeStartOffset, appliedOffsetMode, applyFootIK, m_Loop);
 | |
| 
 | |
| #if UNITY_EDITOR
 | |
|             m_AnimationOffsetPlayable = AnimationOffsetPlayable.Null;
 | |
|             if (root.IsValid() && root.IsPlayableOfType<AnimationOffsetPlayable>())
 | |
|             {
 | |
|                 m_AnimationOffsetPlayable = (AnimationOffsetPlayable)root;
 | |
|             }
 | |
| 
 | |
|             LiveLink();
 | |
| #endif
 | |
| 
 | |
|             return root;
 | |
|         }
 | |
| 
 | |
|         internal static Playable CreatePlayable(PlayableGraph graph, AnimationClip clip, Vector3 positionOffset, Vector3 eulerOffset, bool removeStartOffset, AppliedOffsetMode mode, bool applyFootIK, LoopMode loop)
 | |
|         {
 | |
|             if (clip == null || clip.legacy)
 | |
|                 return Playable.Null;
 | |
| 
 | |
| 
 | |
|             var clipPlayable = AnimationClipPlayable.Create(graph, clip);
 | |
|             clipPlayable.SetRemoveStartOffset(removeStartOffset);
 | |
|             clipPlayable.SetApplyFootIK(applyFootIK);
 | |
|             clipPlayable.SetOverrideLoopTime(loop != LoopMode.UseSourceAsset);
 | |
|             clipPlayable.SetLoopTime(loop == LoopMode.On);
 | |
| 
 | |
|             Playable root = clipPlayable;
 | |
| 
 | |
|             if (ShouldApplyScaleRemove(mode))
 | |
|             {
 | |
|                 var removeScale = AnimationRemoveScalePlayable.Create(graph, 1);
 | |
|                 graph.Connect(root, 0, removeScale, 0);
 | |
|                 removeScale.SetInputWeight(0, 1.0f);
 | |
|                 root = removeScale;
 | |
|             }
 | |
| 
 | |
|             if (ShouldApplyOffset(mode, clip))
 | |
|             {
 | |
|                 var offsetPlayable = AnimationOffsetPlayable.Create(graph, positionOffset, Quaternion.Euler(eulerOffset), 1);
 | |
|                 graph.Connect(root, 0, offsetPlayable, 0);
 | |
|                 offsetPlayable.SetInputWeight(0, 1.0F);
 | |
|                 root = offsetPlayable;
 | |
|             }
 | |
| 
 | |
|             return root;
 | |
|         }
 | |
| 
 | |
|         private static bool ShouldApplyOffset(AppliedOffsetMode mode, AnimationClip clip)
 | |
|         {
 | |
|             if (mode == AppliedOffsetMode.NoRootTransform || mode == AppliedOffsetMode.SceneOffsetLegacy)
 | |
|                 return false;
 | |
| 
 | |
|             return HasRootTransforms(clip);
 | |
|         }
 | |
| 
 | |
|         private static bool ShouldApplyScaleRemove(AppliedOffsetMode mode)
 | |
|         {
 | |
|             return mode == AppliedOffsetMode.SceneOffsetLegacyEditor || mode == AppliedOffsetMode.SceneOffsetLegacy || mode == AppliedOffsetMode.TransformOffsetLegacy;
 | |
|         }
 | |
| 
 | |
| #if UNITY_EDITOR
 | |
|         public void LiveLink()
 | |
|         {
 | |
|             if (m_AnimationOffsetPlayable.IsValid())
 | |
|             {
 | |
|                 m_AnimationOffsetPlayable.SetPosition(position);
 | |
|                 m_AnimationOffsetPlayable.SetRotation(rotation);
 | |
|             }
 | |
|         }
 | |
| 
 | |
| #endif
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns the capabilities of TimelineClips that contain a AnimationPlayableAsset
 | |
|         /// </summary>
 | |
|         public ClipCaps clipCaps
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 var caps = ClipCaps.Extrapolation | ClipCaps.SpeedMultiplier | ClipCaps.Blending;
 | |
|                 if (m_Clip != null && (m_Loop != LoopMode.Off) && (m_Loop != LoopMode.UseSourceAsset || m_Clip.isLooping))
 | |
|                     caps |= ClipCaps.Looping;
 | |
| 
 | |
|                 // empty clips don't support clip in. This allows trim operations to simply become move operations
 | |
|                 if (m_Clip != null && !m_Clip.empty)
 | |
|                     caps |= ClipCaps.ClipIn;
 | |
| 
 | |
|                 return caps;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Resets the offsets to default values
 | |
|         /// </summary>
 | |
|         public void ResetOffsets()
 | |
|         {
 | |
|             position = Vector3.zero;
 | |
|             eulerAngles = Vector3.zero;
 | |
|         }
 | |
| 
 | |
|         /// <inheritdoc/>
 | |
|         public void GatherProperties(PlayableDirector director, IPropertyCollector driver)
 | |
|         {
 | |
|             driver.AddFromClip(m_Clip);
 | |
|         }
 | |
| 
 | |
|         internal static bool HasRootTransforms(AnimationClip clip)
 | |
|         {
 | |
|             if (clip == null || clip.empty)
 | |
|                 return false;
 | |
| 
 | |
|             return clip.hasRootMotion || clip.hasGenericRootTransform || clip.hasMotionCurves || clip.hasRootCurves;
 | |
|         }
 | |
|     }
 | |
| }
 | 
