| using System; |
| using System.Collections.Generic; |
| using UnityEngine.Playables; |
| using UnityEngine.Serialization; |
|
|
| namespace UnityEngine.Timeline |
| { |
| |
| |
| |
| public interface ITimelineClipAsset |
| { |
| |
| |
| |
| ClipCaps clipCaps { get; } |
| } |
|
|
| |
| |
| |
| [Serializable] |
| public partial class TimelineClip : ICurvesOwner, ISerializationCallbackReceiver |
| { |
| |
| |
| |
| public static readonly ClipCaps kDefaultClipCaps = ClipCaps.Blending; |
|
|
| |
| |
| |
| public static readonly float kDefaultClipDurationInSeconds = 5; |
|
|
| |
| |
| |
| public static readonly double kTimeScaleMin = 1.0 / 1000; |
|
|
| |
| |
| |
| public static readonly double kTimeScaleMax = 1000; |
|
|
| internal static readonly string kDefaultCurvesName = "Clip Parameters"; |
|
|
| internal static readonly double kMinDuration = 1 / 60.0; |
|
|
| |
| internal static readonly double kMaxTimeValue = 1000000; |
|
|
| |
| |
| |
| public enum ClipExtrapolation |
| { |
| |
| |
| |
| None, |
|
|
| |
| |
| |
| Hold, |
|
|
| |
| |
| |
| Loop, |
|
|
| |
| |
| |
| PingPong, |
|
|
| |
| |
| |
| Continue |
| }; |
|
|
| |
| |
| |
| public enum BlendCurveMode |
| { |
| |
| |
| |
| Auto, |
|
|
| |
| |
| |
| Manual |
| }; |
|
|
| internal TimelineClip(TrackAsset parent) |
| { |
| |
| 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; |
|
|
| |
| [SerializeField] double m_EaseInDuration; |
| [SerializeField] double m_EaseOutDuration; |
|
|
| |
| [SerializeField] double m_BlendInDuration = -1.0f; |
| [SerializeField] double m_BlendOutDuration = -1.0f; |
|
|
| |
| [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; |
|
|
| |
| [SerializeField] ClipExtrapolation m_PostExtrapolationMode; |
| [SerializeField] ClipExtrapolation m_PreExtrapolationMode; |
| [SerializeField] double m_PostExtrapolationTime; |
| [SerializeField] double m_PreExtrapolationTime; |
|
|
| [SerializeField] string m_DisplayName; |
|
|
| |
| |
| |
| public bool hasPreExtrapolation |
| { |
| get { return m_PreExtrapolationMode != ClipExtrapolation.None && m_PreExtrapolationTime > 0; } |
| } |
|
|
| |
| |
| |
| public bool hasPostExtrapolation |
| { |
| get { return m_PostExtrapolationMode != ClipExtrapolation.None && m_PostExtrapolationTime > 0; } |
| } |
|
|
| |
| |
| |
| 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; |
| } |
| } |
|
|
| |
| |
| |
| 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; |
| } |
| } |
|
|
| |
| |
| |
| public double duration |
| { |
| get { return m_Duration; } |
| set |
| { |
| UpdateDirty(m_Duration, value); |
| m_Duration = Math.Max(SanitizeTimeValue(value, m_Duration), double.Epsilon); |
| } |
| } |
|
|
| |
| |
| |
| public double end |
| { |
| get { return m_Start + m_Duration; } |
| } |
|
|
| |
| |
| |
| 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; |
| } |
| } |
|
|
| |
| |
| |
| public string displayName |
| { |
| get { return m_DisplayName; } |
| set { m_DisplayName = value; } |
| } |
|
|
|
|
| |
| |
| |
| public double clipAssetDuration |
| { |
| get |
| { |
| var playableAsset = m_Asset as IPlayableAsset; |
| return playableAsset != null ? playableAsset.duration : double.MaxValue; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public AnimationClip curves |
| { |
| get { return m_AnimationCurves; } |
| internal set { m_AnimationCurves = value; } |
| } |
|
|
| string ICurvesOwner.defaultCurvesName |
| { |
| get { return kDefaultCurvesName; } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public bool hasCurves |
| { |
| get { return m_AnimationCurves != null && !m_AnimationCurves.empty; } |
| } |
|
|
| |
| |
| |
| public Object asset |
| { |
| get { return m_Asset; } |
| set { m_Asset = value; } |
| } |
|
|
| Object ICurvesOwner.assetOwner |
| { |
| get { return GetParentTrack(); } |
| } |
|
|
| TrackAsset ICurvesOwner.targetTrack |
| { |
| get { return GetParentTrack(); } |
| } |
|
|
| |
| |
| |
| [Obsolete("underlyingAsset property is obsolete. Use asset property instead", true)] |
| public Object underlyingAsset |
| { |
| get { return null; } |
| set { } |
| } |
|
|
| |
| |
| |
| [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); } |
| } |
|
|
| |
| |
| |
| |
| public TrackAsset GetParentTrack() |
| { |
| return m_ParentTrack; |
| } |
|
|
| |
| |
| |
| |
| 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); |
| } |
|
|
| |
| |
| |
| 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; |
| } |
| } |
|
|
| |
| |
| |
| 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; |
| } |
| } |
|
|
| |
| |
| |
| [Obsolete("Use easeOutTime instead (UnityUpgradable) -> easeOutTime", true)] |
| public double eastOutTime |
| { |
| get { return duration - easeOutDuration + m_Start; } |
| } |
|
|
| |
| |
| |
| public double easeOutTime |
| { |
| get { return duration - easeOutDuration + m_Start; } |
| } |
|
|
| |
| |
| |
| public double blendInDuration |
| { |
| get { return clipCaps.HasAny(ClipCaps.Blending) ? m_BlendInDuration : 0; } |
| set { m_BlendInDuration = clipCaps.HasAny(ClipCaps.Blending) ? SanitizeTimeValue(value, m_BlendInDuration) : 0; } |
| } |
|
|
| |
| |
| |
| public double blendOutDuration |
| { |
| get { return clipCaps.HasAny(ClipCaps.Blending) ? m_BlendOutDuration : 0; } |
| set { m_BlendOutDuration = clipCaps.HasAny(ClipCaps.Blending) ? SanitizeTimeValue(value, m_BlendOutDuration) : 0; } |
| } |
|
|
| |
| |
| |
| public BlendCurveMode blendInCurveMode |
| { |
| get { return m_BlendInCurveMode; } |
| set { m_BlendInCurveMode = value; } |
| } |
|
|
| |
| |
| |
| public BlendCurveMode blendOutCurveMode |
| { |
| get { return m_BlendOutCurveMode; } |
| set { m_BlendOutCurveMode = value; } |
| } |
|
|
| |
| |
| |
| public bool hasBlendIn { get { return clipCaps.HasAny(ClipCaps.Blending) && m_BlendInDuration > 0; } } |
|
|
| |
| |
| |
| public bool hasBlendOut { get { return clipCaps.HasAny(ClipCaps.Blending) && m_BlendOutDuration > 0; } } |
|
|
| |
| |
| |
| public AnimationCurve mixInCurve |
| { |
| get |
| { |
| |
| if (m_MixInCurve == null || m_MixInCurve.length < 2) |
| m_MixInCurve = GetDefaultMixInCurve(); |
|
|
| return m_MixInCurve; |
| } |
| set { m_MixInCurve = value; } |
| } |
|
|
| |
| |
| |
| public float mixInPercentage |
| { |
| get { return (float)(mixInDuration / duration); } |
| } |
|
|
| |
| |
| |
| public double mixInDuration |
| { |
| get { return hasBlendIn ? blendInDuration : easeInDuration; } |
| } |
|
|
| |
| |
| |
| public AnimationCurve mixOutCurve |
| { |
| get |
| { |
| if (m_MixOutCurve == null || m_MixOutCurve.length < 2) |
| m_MixOutCurve = GetDefaultMixOutCurve(); |
| return m_MixOutCurve; |
| } |
| set { m_MixOutCurve = value; } |
| } |
|
|
| |
| |
| |
| public double mixOutTime |
| { |
| get { return duration - mixOutDuration + m_Start; } |
| } |
|
|
| |
| |
| |
| public double mixOutDuration |
| { |
| get { return hasBlendOut ? blendOutDuration : easeOutDuration; } |
| } |
|
|
| |
| |
| |
| public float mixOutPercentage |
| { |
| get { return (float)(mixOutDuration / duration); } |
| } |
|
|
| |
| |
| |
| public bool recordable |
| { |
| get { return m_Recordable; } |
| internal set { m_Recordable = value; } |
| } |
|
|
| |
| |
| |
| [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>()); } |
| } |
|
|
| |
| |
| |
| 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()); |
| } |
|
|
| |
| |
| |
| |
| |
| 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; |
| } |
|
|
| |
| |
| |
| |
| |
| 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); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public double ToLocalTime(double time) |
| { |
| if (time < 0) |
| return time; |
|
|
| |
| 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; |
|
|
| |
| time *= timeScale; |
| time += clipIn; |
|
|
| return time; |
| } |
|
|
| |
| |
| |
| |
| |
| public double ToLocalTimeUnbound(double time) |
| { |
| return (time - m_Start) * timeScale + clipIn; |
| } |
|
|
| |
| |
| |
| |
| |
| internal double FromLocalTimeUnbound(double time) |
| { |
| return (time - clipIn) / timeScale + m_Start; |
| } |
|
|
| |
| |
| |
| 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)); |
| } |
|
|
| |
| |
| |
| public ClipExtrapolation postExtrapolationMode |
| { |
| get { return clipCaps.HasAny(ClipCaps.Extrapolation) ? m_PostExtrapolationMode : ClipExtrapolation.None; } |
| internal set { m_PostExtrapolationMode = clipCaps.HasAny(ClipCaps.Extrapolation) ? value : ClipExtrapolation.None; } |
| } |
|
|
| |
| |
| |
| 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; |
| } |
|
|
| |
| |
| |
| |
| |
| public bool IsExtrapolatedTime(double sequenceTime) |
| { |
| return IsPreExtrapolatedTime(sequenceTime) || IsPostExtrapolatedTime(sequenceTime); |
| } |
|
|
| |
| |
| |
| |
| |
| public bool IsPreExtrapolatedTime(double sequenceTime) |
| { |
| return preExtrapolationMode != ClipExtrapolation.None && |
| sequenceTime < m_Start && sequenceTime >= m_Start - m_PreExtrapolationTime; |
| } |
|
|
| |
| |
| |
| |
| |
| public bool IsPostExtrapolatedTime(double sequenceTime) |
| { |
| return postExtrapolationMode != ClipExtrapolation.None && |
| (sequenceTime > end) && (sequenceTime - end < m_PostExtrapolationTime); |
| } |
|
|
| |
| |
| |
| public double extrapolatedStart |
| { |
| get |
| { |
| if (m_PreExtrapolationMode != ClipExtrapolation.None) |
| return m_Start - m_PreExtrapolationTime; |
|
|
| return m_Start; |
| } |
| } |
|
|
| |
| |
| |
| 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; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public void CreateCurves(string curvesClipName) |
| { |
| if (m_AnimationCurves != null) |
| return; |
|
|
| m_AnimationCurves = TimelineCreateUtilities.CreateAnimationClipForTrack(string.IsNullOrEmpty(curvesClipName) ? kDefaultCurvesName : curvesClipName, GetParentTrack(), true); |
| } |
|
|
| |
| |
| |
| void ISerializationCallbackReceiver.OnBeforeSerialize() |
| { |
| m_Version = k_LatestVersion; |
| } |
|
|
| |
| |
| |
| void ISerializationCallbackReceiver.OnAfterDeserialize() |
| { |
| if (m_Version < k_LatestVersion) |
| { |
| UpgradeToLatestVersion(); |
| } |
| } |
|
|
| |
| |
| |
| |
| public override string ToString() |
| { |
| return UnityString.Format("{0} ({1:F2}, {2:F2}):{3:F2} | {4}", displayName, start, end, clipIn, GetParentTrack()); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| 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 |
| }; |
| } |
|
|