File size: 10,773 Bytes
18a519f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 | 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;
}
}
}
|