You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
901 lines
31 KiB
901 lines
31 KiB
using System; |
|
using System.Collections.Generic; |
|
using UnityEngine.Playables; |
|
using UnityEngine.Serialization; |
|
|
|
namespace UnityEngine.Timeline |
|
{ |
|
/// <summary> |
|
/// Implement this interface to support advanced features of timeline clips. |
|
/// </summary> |
|
public interface ITimelineClipAsset |
|
{ |
|
/// <summary> |
|
/// Returns a description of the features supported by clips with PlayableAssets implementing this interface. |
|
/// </summary> |
|
ClipCaps clipCaps { get; } |
|
} |
|
|
|
/// <summary> |
|
/// Represents a clip on the timeline. |
|
/// </summary> |
|
[Serializable] |
|
public partial class TimelineClip : ICurvesOwner, ISerializationCallbackReceiver |
|
{ |
|
/// <summary> |
|
/// The default capabilities for a clip |
|
/// </summary> |
|
public static readonly ClipCaps kDefaultClipCaps = ClipCaps.Blending; |
|
|
|
/// <summary> |
|
/// The default length of a clip in seconds. |
|
/// </summary> |
|
public static readonly float kDefaultClipDurationInSeconds = 5; |
|
|
|
/// <summary> |
|
/// The minimum timescale allowed on a clip |
|
/// </summary> |
|
public static readonly double kTimeScaleMin = 1.0 / 1000; |
|
|
|
/// <summary> |
|
/// The maximum timescale allowed on a clip |
|
/// </summary> |
|
public static readonly double kTimeScaleMax = 1000; |
|
|
|
internal static readonly string kDefaultCurvesName = "Clip Parameters"; |
|
|
|
internal static readonly double kMinDuration = 1 / 60.0; |
|
|
|
// constant representing the longest possible sequence duration |
|
internal static readonly double kMaxTimeValue = 1000000; // more than a week's time, and within numerical precision boundaries |
|
|
|
/// <summary> |
|
/// How the clip handles time outside its start and end range. |
|
/// </summary> |
|
public enum ClipExtrapolation |
|
{ |
|
/// <summary> |
|
/// No extrapolation is applied. |
|
/// </summary> |
|
None, |
|
|
|
/// <summary> |
|
/// Hold the time at the end value of the clip. |
|
/// </summary> |
|
Hold, |
|
|
|
/// <summary> |
|
/// Repeat time values outside the start/end range. |
|
/// </summary> |
|
Loop, |
|
|
|
/// <summary> |
|
/// Repeat time values outside the start/end range, reversing direction at each loop |
|
/// </summary> |
|
PingPong, |
|
|
|
/// <summary> |
|
/// Time values are passed in without modification, extending beyond the clips range |
|
/// </summary> |
|
Continue |
|
}; |
|
|
|
/// <summary> |
|
/// How blend curves are treated in an overlap |
|
/// </summary> |
|
public enum BlendCurveMode |
|
{ |
|
/// <summary> |
|
/// The curve is normalized against the opposing clip |
|
/// </summary> |
|
Auto, |
|
|
|
/// <summary> |
|
/// The blend curve is fixed. |
|
/// </summary> |
|
Manual |
|
}; |
|
|
|
internal TimelineClip(TrackAsset parent) |
|
{ |
|
// parent clip into track |
|
SetParentTrack_Internal(parent); |
|
} |
|
|
|
[SerializeField] double m_Start; |
|
[SerializeField] double m_ClipIn; |
|
[SerializeField] Object m_Asset; |
|
[SerializeField] [FormerlySerializedAs("m_HackDuration")] double m_Duration; |
|
[SerializeField] double m_TimeScale = 1.0; |
|
[SerializeField] TrackAsset m_ParentTrack; |
|
|
|
// for mixing out scripts - default is no mix out (i.e. flat) |
|
[SerializeField] double m_EaseInDuration; |
|
[SerializeField] double m_EaseOutDuration; |
|
|
|
// the blend durations override ease in / out durations |
|
[SerializeField] double m_BlendInDuration = -1.0f; |
|
[SerializeField] double m_BlendOutDuration = -1.0f; |
|
|
|
// doubles as ease in/out and blend in/out curves |
|
[SerializeField] AnimationCurve m_MixInCurve; |
|
[SerializeField] AnimationCurve m_MixOutCurve; |
|
|
|
[SerializeField] BlendCurveMode m_BlendInCurveMode = BlendCurveMode.Auto; |
|
[SerializeField] BlendCurveMode m_BlendOutCurveMode = BlendCurveMode.Auto; |
|
|
|
[SerializeField] List<string> m_ExposedParameterNames; |
|
[SerializeField] AnimationClip m_AnimationCurves; |
|
|
|
[SerializeField] bool m_Recordable; |
|
|
|
// extrapolation |
|
[SerializeField] ClipExtrapolation m_PostExtrapolationMode; |
|
[SerializeField] ClipExtrapolation m_PreExtrapolationMode; |
|
[SerializeField] double m_PostExtrapolationTime; |
|
[SerializeField] double m_PreExtrapolationTime; |
|
|
|
[SerializeField] string m_DisplayName; |
|
|
|
/// <summary> |
|
/// Is the clip being extrapolated before its start time? |
|
/// </summary> |
|
public bool hasPreExtrapolation |
|
{ |
|
get { return m_PreExtrapolationMode != ClipExtrapolation.None && m_PreExtrapolationTime > 0; } |
|
} |
|
|
|
/// <summary> |
|
/// Is the clip being extrapolated past its end time? |
|
/// </summary> |
|
public bool hasPostExtrapolation |
|
{ |
|
get { return m_PostExtrapolationMode != ClipExtrapolation.None && m_PostExtrapolationTime > 0; } |
|
} |
|
|
|
/// <summary> |
|
/// A speed multiplier for the clip; |
|
/// </summary> |
|
public double timeScale |
|
{ |
|
get { return clipCaps.HasAny(ClipCaps.SpeedMultiplier) ? Math.Max(kTimeScaleMin, Math.Min(m_TimeScale, kTimeScaleMax)) : 1.0; } |
|
set |
|
{ |
|
UpdateDirty(m_TimeScale, value); |
|
m_TimeScale = clipCaps.HasAny(ClipCaps.SpeedMultiplier) ? Math.Max(kTimeScaleMin, Math.Min(value, kTimeScaleMax)) : 1.0; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The start time, in seconds, of the clip |
|
/// </summary> |
|
public double start |
|
{ |
|
get { return m_Start; } |
|
set |
|
{ |
|
UpdateDirty(value, m_Start); |
|
var newValue = Math.Max(SanitizeTimeValue(value, m_Start), 0); |
|
if (m_ParentTrack != null && m_Start != newValue) |
|
{ |
|
m_ParentTrack.OnClipMove(); |
|
} |
|
m_Start = newValue; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The length, in seconds, of the clip |
|
/// </summary> |
|
public double duration |
|
{ |
|
get { return m_Duration; } |
|
set |
|
{ |
|
UpdateDirty(m_Duration, value); |
|
m_Duration = Math.Max(SanitizeTimeValue(value, m_Duration), double.Epsilon); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The end time, in seconds of the clip |
|
/// </summary> |
|
public double end |
|
{ |
|
get { return m_Start + m_Duration; } |
|
} |
|
|
|
/// <summary> |
|
/// Local offset time of the clip. |
|
/// </summary> |
|
public double clipIn |
|
{ |
|
get { return clipCaps.HasAny(ClipCaps.ClipIn) ? m_ClipIn : 0; } |
|
set |
|
{ |
|
UpdateDirty(m_ClipIn, value); |
|
m_ClipIn = clipCaps.HasAny(ClipCaps.ClipIn) ? Math.Max(Math.Min(SanitizeTimeValue(value, m_ClipIn), kMaxTimeValue), 0.0) : 0; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The name displayed on the clip |
|
/// </summary> |
|
public string displayName |
|
{ |
|
get { return m_DisplayName; } |
|
set { m_DisplayName = value; } |
|
} |
|
|
|
|
|
/// <summary> |
|
/// The length, in seconds, of the PlayableAsset attached to the clip. |
|
/// </summary> |
|
public double clipAssetDuration |
|
{ |
|
get |
|
{ |
|
var playableAsset = m_Asset as IPlayableAsset; |
|
return playableAsset != null ? playableAsset.duration : double.MaxValue; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// An animation clip containing animated properties of the attached PlayableAsset |
|
/// </summary> |
|
/// <remarks> |
|
/// This is where animated clip properties are stored. |
|
/// </remarks> |
|
public AnimationClip curves |
|
{ |
|
get { return m_AnimationCurves; } |
|
internal set { m_AnimationCurves = value; } |
|
} |
|
|
|
string ICurvesOwner.defaultCurvesName |
|
{ |
|
get { return kDefaultCurvesName; } |
|
} |
|
|
|
/// <summary> |
|
/// Whether this clip contains animated properties for the attached PlayableAsset. |
|
/// </summary> |
|
/// <remarks> |
|
/// This property is false if the curves property is null or if it contains no information. |
|
/// </remarks> |
|
public bool hasCurves |
|
{ |
|
get { return m_AnimationCurves != null && !m_AnimationCurves.empty; } |
|
} |
|
|
|
/// <summary> |
|
/// The PlayableAsset attached to the clip. |
|
/// </summary> |
|
public Object asset |
|
{ |
|
get { return m_Asset; } |
|
set { m_Asset = value; } |
|
} |
|
|
|
Object ICurvesOwner.assetOwner |
|
{ |
|
get { return GetParentTrack(); } |
|
} |
|
|
|
TrackAsset ICurvesOwner.targetTrack |
|
{ |
|
get { return GetParentTrack(); } |
|
} |
|
|
|
/// <summary> |
|
/// underlyingAsset property is obsolete. Use asset property instead |
|
/// </summary> |
|
[Obsolete("underlyingAsset property is obsolete. Use asset property instead", true)] |
|
public Object underlyingAsset |
|
{ |
|
get { return null; } |
|
set { } |
|
} |
|
|
|
/// <summary> |
|
/// Returns the TrackAsset to which this clip is attached. |
|
/// </summary> |
|
[Obsolete("parentTrack is deprecated and will be removed in a future release. Use " + nameof(GetParentTrack) + "() and " + nameof(TimelineClipExtensions) + "::" + nameof(TimelineClipExtensions.MoveToTrack) + "() or " + nameof(TimelineClipExtensions) + "::" + nameof(TimelineClipExtensions.TryMoveToTrack) + "() instead.", false)] |
|
public TrackAsset parentTrack |
|
{ |
|
get { return m_ParentTrack; } |
|
set { SetParentTrack_Internal(value); } |
|
} |
|
|
|
/// <summary> |
|
/// Get the TrackAsset to which this clip is attached. |
|
/// </summary> |
|
/// <returns>the parent TrackAsset</returns> |
|
public TrackAsset GetParentTrack() |
|
{ |
|
return m_ParentTrack; |
|
} |
|
|
|
/// <summary> |
|
/// Sets the parent track without performing any validation. To ensure a valid change use TimelineClipExtensions.TrySetParentTrack(TrackAsset) instead. |
|
/// </summary> |
|
/// <param name="newParentTrack"></param> |
|
internal void SetParentTrack_Internal(TrackAsset newParentTrack) |
|
{ |
|
if (m_ParentTrack == newParentTrack) |
|
return; |
|
|
|
if (m_ParentTrack != null) |
|
m_ParentTrack.RemoveClip(this); |
|
|
|
m_ParentTrack = newParentTrack; |
|
|
|
if (m_ParentTrack != null) |
|
m_ParentTrack.AddClip(this); |
|
} |
|
|
|
/// <summary> |
|
/// The ease in duration of the timeline clip in seconds. This only applies if the start of the clip is not overlapping. |
|
/// </summary> |
|
public double easeInDuration |
|
{ |
|
get |
|
{ |
|
var availableDuration = hasBlendOut ? duration - m_BlendOutDuration : duration; |
|
return clipCaps.HasAny(ClipCaps.Blending) ? Math.Min(Math.Max(m_EaseInDuration, 0), availableDuration) : 0; |
|
} |
|
set |
|
{ |
|
var availableDuration = hasBlendOut ? duration - m_BlendOutDuration : duration; |
|
m_EaseInDuration = clipCaps.HasAny(ClipCaps.Blending) ? Math.Max(0, Math.Min(SanitizeTimeValue(value, m_EaseInDuration), availableDuration)) : 0; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The ease out duration of the timeline clip in seconds. This only applies if the end of the clip is not overlapping. |
|
/// </summary> |
|
public double easeOutDuration |
|
{ |
|
get |
|
{ |
|
var availableDuration = hasBlendIn ? duration - m_BlendInDuration : duration; |
|
return clipCaps.HasAny(ClipCaps.Blending) ? Math.Min(Math.Max(m_EaseOutDuration, 0), availableDuration) : 0; |
|
} |
|
set |
|
{ |
|
var availableDuration = hasBlendIn ? duration - m_BlendInDuration : duration; |
|
m_EaseOutDuration = clipCaps.HasAny(ClipCaps.Blending) ? Math.Max(0, Math.Min(SanitizeTimeValue(value, m_EaseOutDuration), availableDuration)) : 0; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// eastOutTime property is obsolete use easeOutTime property instead |
|
/// </summary> |
|
[Obsolete("Use easeOutTime instead (UnityUpgradable) -> easeOutTime", true)] |
|
public double eastOutTime |
|
{ |
|
get { return duration - easeOutDuration + m_Start; } |
|
} |
|
|
|
/// <summary> |
|
/// The time in seconds that the ease out begins |
|
/// </summary> |
|
public double easeOutTime |
|
{ |
|
get { return duration - easeOutDuration + m_Start; } |
|
} |
|
|
|
/// <summary> |
|
/// The amount of overlap in seconds on the start of a clip. |
|
/// </summary> |
|
public double blendInDuration |
|
{ |
|
get { return clipCaps.HasAny(ClipCaps.Blending) ? m_BlendInDuration : 0; } |
|
set { m_BlendInDuration = clipCaps.HasAny(ClipCaps.Blending) ? SanitizeTimeValue(value, m_BlendInDuration) : 0; } |
|
} |
|
|
|
/// <summary> |
|
/// The amount of overlap in seconds at the end of a clip. |
|
/// </summary> |
|
public double blendOutDuration |
|
{ |
|
get { return clipCaps.HasAny(ClipCaps.Blending) ? m_BlendOutDuration : 0; } |
|
set { m_BlendOutDuration = clipCaps.HasAny(ClipCaps.Blending) ? SanitizeTimeValue(value, m_BlendOutDuration) : 0; } |
|
} |
|
|
|
/// <summary> |
|
/// The mode for calculating the blend curve of the overlap at the start of the clip |
|
/// </summary> |
|
public BlendCurveMode blendInCurveMode |
|
{ |
|
get { return m_BlendInCurveMode; } |
|
set { m_BlendInCurveMode = value; } |
|
} |
|
|
|
/// <summary> |
|
/// The mode for calculating the blend curve of the overlap at the end of the clip |
|
/// </summary> |
|
public BlendCurveMode blendOutCurveMode |
|
{ |
|
get { return m_BlendOutCurveMode; } |
|
set { m_BlendOutCurveMode = value; } |
|
} |
|
|
|
/// <summary> |
|
/// Returns whether the clip is blending in |
|
/// </summary> |
|
public bool hasBlendIn { get { return clipCaps.HasAny(ClipCaps.Blending) && m_BlendInDuration > 0; } } |
|
|
|
/// <summary> |
|
/// Returns whether the clip is blending out |
|
/// </summary> |
|
public bool hasBlendOut { get { return clipCaps.HasAny(ClipCaps.Blending) && m_BlendOutDuration > 0; } } |
|
|
|
/// <summary> |
|
/// The animation curve used for calculating weights during an ease in or a blend in. |
|
/// </summary> |
|
public AnimationCurve mixInCurve |
|
{ |
|
get |
|
{ |
|
// auto fix broken curves |
|
if (m_MixInCurve == null || m_MixInCurve.length < 2) |
|
m_MixInCurve = GetDefaultMixInCurve(); |
|
|
|
return m_MixInCurve; |
|
} |
|
set { m_MixInCurve = value; } |
|
} |
|
|
|
/// <summary> |
|
/// The amount of the clip being used for ease or blend in as a percentage |
|
/// </summary> |
|
public float mixInPercentage |
|
{ |
|
get { return (float)(mixInDuration / duration); } |
|
} |
|
|
|
/// <summary> |
|
/// The amount of the clip blending or easing in, in seconds |
|
/// </summary> |
|
public double mixInDuration |
|
{ |
|
get { return hasBlendIn ? blendInDuration : easeInDuration; } |
|
} |
|
|
|
/// <summary> |
|
/// The animation curve used for calculating weights during an ease out or a blend out. |
|
/// </summary> |
|
public AnimationCurve mixOutCurve |
|
{ |
|
get |
|
{ |
|
if (m_MixOutCurve == null || m_MixOutCurve.length < 2) |
|
m_MixOutCurve = GetDefaultMixOutCurve(); |
|
return m_MixOutCurve; |
|
} |
|
set { m_MixOutCurve = value; } |
|
} |
|
|
|
/// <summary> |
|
/// The time in seconds that an ease out or blend out starts |
|
/// </summary> |
|
public double mixOutTime |
|
{ |
|
get { return duration - mixOutDuration + m_Start; } |
|
} |
|
|
|
/// <summary> |
|
/// The amount of the clip blending or easing out, in seconds |
|
/// </summary> |
|
public double mixOutDuration |
|
{ |
|
get { return hasBlendOut ? blendOutDuration : easeOutDuration; } |
|
} |
|
|
|
/// <summary> |
|
/// The amount of the clip being used for ease or blend out as a percentage |
|
/// </summary> |
|
public float mixOutPercentage |
|
{ |
|
get { return (float)(mixOutDuration / duration); } |
|
} |
|
|
|
/// <summary> |
|
/// Returns whether this clip is recordable in editor |
|
/// </summary> |
|
public bool recordable |
|
{ |
|
get { return m_Recordable; } |
|
internal set { m_Recordable = value; } |
|
} |
|
|
|
/// <summary> |
|
/// exposedParameter is deprecated and will be removed in a future release |
|
/// </summary> |
|
[Obsolete("exposedParameter is deprecated and will be removed in a future release", true)] |
|
public List<string> exposedParameters |
|
{ |
|
get { return m_ExposedParameterNames ?? (m_ExposedParameterNames = new List<string>()); } |
|
} |
|
|
|
/// <summary> |
|
/// Returns the capabilities supported by this clip. |
|
/// </summary> |
|
public ClipCaps clipCaps |
|
{ |
|
get |
|
{ |
|
var clipAsset = asset as ITimelineClipAsset; |
|
return (clipAsset != null) ? clipAsset.clipCaps : kDefaultClipCaps; |
|
} |
|
} |
|
|
|
internal int Hash() |
|
{ |
|
return HashUtility.CombineHash(m_Start.GetHashCode(), |
|
m_Duration.GetHashCode(), |
|
m_TimeScale.GetHashCode(), |
|
m_ClipIn.GetHashCode(), |
|
((int)m_PreExtrapolationMode).GetHashCode(), |
|
((int)m_PostExtrapolationMode).GetHashCode()); |
|
} |
|
|
|
/// <summary> |
|
/// Given a time, returns the weight from the mix out |
|
/// </summary> |
|
/// <param name="time">Time (relative to the timeline)</param> |
|
/// <returns></returns> |
|
public float EvaluateMixOut(double time) |
|
{ |
|
if (!clipCaps.HasAny(ClipCaps.Blending)) |
|
return 1.0f; |
|
|
|
if (mixOutDuration > Mathf.Epsilon) |
|
{ |
|
var perc = (float)(time - mixOutTime) / (float)mixOutDuration; |
|
perc = Mathf.Clamp01(mixOutCurve.Evaluate(perc)); |
|
return perc; |
|
} |
|
return 1.0f; |
|
} |
|
|
|
/// <summary> |
|
/// Given a time, returns the weight from the mix in |
|
/// </summary> |
|
/// <param name="time">Time (relative to the timeline)</param> |
|
/// <returns></returns> |
|
public float EvaluateMixIn(double time) |
|
{ |
|
if (!clipCaps.HasAny(ClipCaps.Blending)) |
|
return 1.0f; |
|
|
|
if (mixInDuration > Mathf.Epsilon) |
|
{ |
|
var perc = (float)(time - m_Start) / (float)mixInDuration; |
|
perc = Mathf.Clamp01(mixInCurve.Evaluate(perc)); |
|
return perc; |
|
} |
|
return 1.0f; |
|
} |
|
|
|
static AnimationCurve GetDefaultMixInCurve() |
|
{ |
|
return AnimationCurve.EaseInOut(0, 0, 1, 1); |
|
} |
|
|
|
static AnimationCurve GetDefaultMixOutCurve() |
|
{ |
|
return AnimationCurve.EaseInOut(0, 1, 1, 0); |
|
} |
|
|
|
/// <summary> |
|
/// Converts from global time to a clips local time. |
|
/// </summary> |
|
/// <param name="time">time relative to the timeline</param> |
|
/// <returns> |
|
/// The local time with extrapolation applied |
|
/// </returns> |
|
public double ToLocalTime(double time) |
|
{ |
|
if (time < 0) |
|
return time; |
|
|
|
// handle Extrapolation |
|
if (IsPreExtrapolatedTime(time)) |
|
time = GetExtrapolatedTime(time - m_Start, m_PreExtrapolationMode, m_Duration); |
|
else if (IsPostExtrapolatedTime(time)) |
|
time = GetExtrapolatedTime(time - m_Start, m_PostExtrapolationMode, m_Duration); |
|
else |
|
time -= m_Start; |
|
|
|
// handle looping and time scale within the clip |
|
time *= timeScale; |
|
time += clipIn; |
|
|
|
return time; |
|
} |
|
|
|
/// <summary> |
|
/// Converts from global time to local time of the clip |
|
/// </summary> |
|
/// <param name="time">The time relative to the timeline</param> |
|
/// <returns>The local time, ignoring any extrapolation or bounds</returns> |
|
public double ToLocalTimeUnbound(double time) |
|
{ |
|
return (time - m_Start) * timeScale + clipIn; |
|
} |
|
|
|
/// <summary> |
|
/// Converts from local time of the clip to global time |
|
/// </summary> |
|
/// <param name="time">Time relative to the clip</param> |
|
/// <returns>The time relative to the timeline</returns> |
|
internal double FromLocalTimeUnbound(double time) |
|
{ |
|
return (time - clipIn) / timeScale + m_Start; |
|
} |
|
|
|
/// <summary> |
|
/// If this contains an animation asset, returns the animation clip attached. Otherwise returns null. |
|
/// </summary> |
|
public AnimationClip animationClip |
|
{ |
|
get |
|
{ |
|
if (m_Asset == null) |
|
return null; |
|
|
|
var playableAsset = m_Asset as AnimationPlayableAsset; |
|
return playableAsset != null ? playableAsset.clip : null; |
|
} |
|
} |
|
|
|
static double SanitizeTimeValue(double value, double defaultValue) |
|
{ |
|
if (double.IsInfinity(value) || double.IsNaN(value)) |
|
{ |
|
Debug.LogError("Invalid time value assigned"); |
|
return defaultValue; |
|
} |
|
|
|
return Math.Max(-kMaxTimeValue, Math.Min(kMaxTimeValue, value)); |
|
} |
|
|
|
/// <summary> |
|
/// Returns whether the clip is being extrapolated past the end time. |
|
/// </summary> |
|
public ClipExtrapolation postExtrapolationMode |
|
{ |
|
get { return clipCaps.HasAny(ClipCaps.Extrapolation) ? m_PostExtrapolationMode : ClipExtrapolation.None; } |
|
internal set { m_PostExtrapolationMode = clipCaps.HasAny(ClipCaps.Extrapolation) ? value : ClipExtrapolation.None; } |
|
} |
|
|
|
/// <summary> |
|
/// Returns whether the clip is being extrapolated before the start time. |
|
/// </summary> |
|
public ClipExtrapolation preExtrapolationMode |
|
{ |
|
get { return clipCaps.HasAny(ClipCaps.Extrapolation) ? m_PreExtrapolationMode : ClipExtrapolation.None; } |
|
internal set { m_PreExtrapolationMode = clipCaps.HasAny(ClipCaps.Extrapolation) ? value : ClipExtrapolation.None; } |
|
} |
|
|
|
internal void SetPostExtrapolationTime(double time) |
|
{ |
|
m_PostExtrapolationTime = time; |
|
} |
|
|
|
internal void SetPreExtrapolationTime(double time) |
|
{ |
|
m_PreExtrapolationTime = time; |
|
} |
|
|
|
/// <summary> |
|
/// Given a time, returns whether it falls within the clip's extrapolation |
|
/// </summary> |
|
/// <param name="sequenceTime">The time relative to the timeline</param> |
|
/// <returns>True if <paramref name="sequenceTime"/> is within the clip extrapolation</returns> |
|
public bool IsExtrapolatedTime(double sequenceTime) |
|
{ |
|
return IsPreExtrapolatedTime(sequenceTime) || IsPostExtrapolatedTime(sequenceTime); |
|
} |
|
|
|
/// <summary> |
|
/// Given a time, returns whether it falls within the clip's pre-extrapolation |
|
/// </summary> |
|
/// <param name="sequenceTime">The time relative to the timeline</param> |
|
/// <returns>True if <paramref name="sequenceTime"/> is within the clip pre-extrapolation</returns> |
|
public bool IsPreExtrapolatedTime(double sequenceTime) |
|
{ |
|
return preExtrapolationMode != ClipExtrapolation.None && |
|
sequenceTime < m_Start && sequenceTime >= m_Start - m_PreExtrapolationTime; |
|
} |
|
|
|
/// <summary> |
|
/// Given a time, returns whether it falls within the clip's post-extrapolation |
|
/// </summary> |
|
/// <param name="sequenceTime">The time relative to the timeline</param> |
|
/// <returns>True if <paramref name="sequenceTime"/> is within the clip post-extrapolation</returns> |
|
public bool IsPostExtrapolatedTime(double sequenceTime) |
|
{ |
|
return postExtrapolationMode != ClipExtrapolation.None && |
|
(sequenceTime > end) && (sequenceTime - end < m_PostExtrapolationTime); |
|
} |
|
|
|
/// <summary> |
|
/// The start time of the clip, accounting for pre-extrapolation |
|
/// </summary> |
|
public double extrapolatedStart |
|
{ |
|
get |
|
{ |
|
if (m_PreExtrapolationMode != ClipExtrapolation.None) |
|
return m_Start - m_PreExtrapolationTime; |
|
|
|
return m_Start; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The length of the clip in seconds, including extrapolation. |
|
/// </summary> |
|
public double extrapolatedDuration |
|
{ |
|
get |
|
{ |
|
double length = m_Duration; |
|
|
|
if (m_PostExtrapolationMode != ClipExtrapolation.None) |
|
length += Math.Min(m_PostExtrapolationTime, kMaxTimeValue); |
|
|
|
if (m_PreExtrapolationMode != ClipExtrapolation.None) |
|
length += m_PreExtrapolationTime; |
|
|
|
return length; |
|
} |
|
} |
|
|
|
static double GetExtrapolatedTime(double time, ClipExtrapolation mode, double duration) |
|
{ |
|
if (duration == 0) |
|
return 0; |
|
|
|
switch (mode) |
|
{ |
|
case ClipExtrapolation.None: |
|
break; |
|
|
|
case ClipExtrapolation.Loop: |
|
if (time < 0) |
|
time = duration - (-time % duration); |
|
else if (time > duration) |
|
time %= duration; |
|
break; |
|
|
|
case ClipExtrapolation.Hold: |
|
if (time < 0) |
|
return 0; |
|
if (time > duration) |
|
return duration; |
|
break; |
|
|
|
case ClipExtrapolation.PingPong: |
|
if (time < 0) |
|
{ |
|
time = duration * 2 - (-time % (duration * 2)); |
|
time = duration - Math.Abs(time - duration); |
|
} |
|
else |
|
{ |
|
time = time % (duration * 2.0); |
|
time = duration - Math.Abs(time - duration); |
|
} |
|
break; |
|
|
|
case ClipExtrapolation.Continue: |
|
break; |
|
} |
|
return time; |
|
} |
|
|
|
/// <summary> |
|
/// Creates an AnimationClip to store animated properties for the attached PlayableAsset. |
|
/// </summary> |
|
/// <remarks> |
|
/// If curves already exists for this clip, this method produces no result regardless of the |
|
/// value specified for curvesClipName. |
|
/// </remarks> |
|
/// <remarks> |
|
/// When used from the editor, this method attempts to save the created curves clip to the TimelineAsset. |
|
/// The TimelineAsset must already exist in the AssetDatabase to save the curves clip. If the TimelineAsset |
|
/// does not exist, the curves clip is still created but it is not saved. |
|
/// </remarks> |
|
/// <param name="curvesClipName"> |
|
/// The name of the AnimationClip to create. |
|
/// This method does not ensure unique names. If you want a unique clip name, you must provide one. |
|
/// See ObjectNames.GetUniqueName for information on a method that creates unique names. |
|
/// </param> |
|
public void CreateCurves(string curvesClipName) |
|
{ |
|
if (m_AnimationCurves != null) |
|
return; |
|
|
|
m_AnimationCurves = TimelineCreateUtilities.CreateAnimationClipForTrack(string.IsNullOrEmpty(curvesClipName) ? kDefaultCurvesName : curvesClipName, GetParentTrack(), true); |
|
} |
|
|
|
/// <summary> |
|
/// Called before Unity serializes this object. |
|
/// </summary> |
|
void ISerializationCallbackReceiver.OnBeforeSerialize() |
|
{ |
|
m_Version = k_LatestVersion; |
|
} |
|
|
|
/// <summary> |
|
/// Called after Unity deserializes this object. |
|
/// </summary> |
|
void ISerializationCallbackReceiver.OnAfterDeserialize() |
|
{ |
|
if (m_Version < k_LatestVersion) |
|
{ |
|
UpgradeToLatestVersion(); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Outputs a more readable representation of the timeline clip as a string |
|
/// </summary> |
|
/// <returns></returns> |
|
public override string ToString() |
|
{ |
|
return UnityString.Format("{0} ({1:F2}, {2:F2}):{3:F2} | {4}", displayName, start, end, clipIn, GetParentTrack()); |
|
} |
|
|
|
/// <summary> |
|
/// Use this method to adjust ease in and ease out values to avoid overlapping. |
|
/// </summary> |
|
/// <remarks> |
|
/// Ease values will be adjusted to respect the ratio between ease in and ease out. |
|
/// </remarks> |
|
public void ConformEaseValues() |
|
{ |
|
if (m_EaseInDuration + m_EaseOutDuration > duration) |
|
{ |
|
var ratio = CalculateEasingRatio(m_EaseInDuration, m_EaseOutDuration); |
|
m_EaseInDuration = duration * ratio; |
|
m_EaseOutDuration = duration * (1.0 - ratio); |
|
} |
|
} |
|
|
|
static double CalculateEasingRatio(double easeIn, double easeOut) |
|
{ |
|
if (Math.Abs(easeIn - easeOut) < TimeUtility.kTimeEpsilon) |
|
return 0.5; |
|
|
|
if (easeIn == 0.0) |
|
return 0.0; |
|
|
|
if (easeOut == 0.0) |
|
return 1.0; |
|
|
|
return easeIn / (easeIn + easeOut); |
|
} |
|
|
|
#if UNITY_EDITOR |
|
internal int DirtyIndex { get; private set; } |
|
internal void MarkDirty() |
|
{ |
|
DirtyIndex++; |
|
} |
|
|
|
void UpdateDirty(double oldValue, double newValue) |
|
{ |
|
if (oldValue != newValue) |
|
DirtyIndex++; |
|
} |
|
|
|
#else |
|
void UpdateDirty(double oldValue, double newValue) { } |
|
#endif |
|
}; |
|
}
|
|
|