Maze / Library /PackageCache /com.unity.timeline@1.6.5 /Runtime /Animation /AnimationPlayableAsset.cs
| using System.Collections.Generic; | |
| using UnityEngine.Animations; | |
| using UnityEngine.Playables; | |
| namespace UnityEngine.Timeline | |
| { | |
| /// <summary> | |
| /// A Playable Asset that represents a single AnimationClip clip. | |
| /// </summary> | |
| [] | |
| 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> | |
| [] | |
| UseSourceAsset = 0, | |
| /// <summary> | |
| /// The source AnimationClip loops during playback. | |
| /// </summary> | |
| [] | |
| On = 1, | |
| /// <summary> | |
| /// The source AnimationClip does not loop during playback. | |
| /// </summary> | |
| [] | |
| Off = 2 | |
| } | |
| [] private AnimationClip m_Clip; | |
| [] private Vector3 m_Position = Vector3.zero; | |
| [] private Vector3 m_EulerAngles = Vector3.zero; | |
| [] private bool m_UseTrackMatchFields = true; | |
| [] private MatchTargetFields m_MatchTargetFields = MatchTargetFieldConstants.All; | |
| [] private bool m_RemoveStartOffset = true; // set by animation track prior to compilation | |
| [] private bool m_ApplyFootIK = true; | |
| [] private LoopMode m_Loop = LoopMode.UseSourceAsset; | |
| private AnimationOffsetPlayable m_AnimationOffsetPlayable; | |
| /// <summary> | |
| /// The translational offset of the clip | |
| /// </summary> | |
| public Vector3 position | |
| { | |
| get | |
| { | |
| return m_Position; | |
| } | |
| set | |
| { | |
| m_Position = value; | |
| if (m_AnimationOffsetPlayable.IsValid()) | |
| m_AnimationOffsetPlayable.SetPosition(position); | |
| } | |
| } | |
| /// <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 (m_AnimationOffsetPlayable.IsValid()) | |
| m_AnimationOffsetPlayable.SetRotation(value); | |
| } | |
| } | |
| /// <summary> | |
| /// The rotational offset of the clip, expressed in Euler angles | |
| /// </summary> | |
| public Vector3 eulerAngles | |
| { | |
| get { return m_EulerAngles; } | |
| set | |
| { | |
| m_EulerAngles = value; | |
| if (m_AnimationOffsetPlayable.IsValid()) | |
| m_AnimationOffsetPlayable.SetRotation(rotation); | |
| } | |
| } | |
| /// <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); | |
| m_AnimationOffsetPlayable = AnimationOffsetPlayable.Null; | |
| if (root.IsValid() && root.IsPlayableOfType<AnimationOffsetPlayable>()) | |
| { | |
| m_AnimationOffsetPlayable = (AnimationOffsetPlayable)root; | |
| } | |
| LiveLink(); | |
| 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; | |
| } | |
| public void LiveLink() | |
| { | |
| if (m_AnimationOffsetPlayable.IsValid()) | |
| { | |
| m_AnimationOffsetPlayable.SetPosition(position); | |
| m_AnimationOffsetPlayable.SetRotation(rotation); | |
| } | |
| } | |
| /// <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; | |
| } | |
| } | |
| } | |