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