A game about forced loneliness, made by TACStudios
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}