| using System; |
| using System.Collections.Generic; |
| using UnityEngine.Playables; |
|
|
| namespace UnityEngine.Timeline |
| { |
| |
| |
| |
| [ExcludeFromPreset] |
| [Serializable] |
| [TimelineHelpURL(typeof(TimelineAsset))] |
| public partial class TimelineAsset : PlayableAsset, ISerializationCallbackReceiver, ITimelineClipAsset, IPropertyPreview |
| { |
| |
| |
| |
| public enum DurationMode |
| { |
| |
| |
| |
| BasedOnClips, |
| |
| |
| |
| FixedLength |
| } |
|
|
| |
| |
| |
| [Serializable] |
| public class EditorSettings |
| { |
| internal static readonly double kMinFrameRate = TimeUtility.kFrameRateEpsilon; |
| internal static readonly double kMaxFrameRate = 1000.0; |
| internal static readonly double kDefaultFrameRate = 60.0; |
| [HideInInspector, SerializeField, FrameRateField] double m_Framerate = kDefaultFrameRate; |
| [HideInInspector, SerializeField] bool m_ScenePreview = true; |
|
|
| |
| |
| |
| [Obsolete("EditorSettings.fps has been deprecated. Use editorSettings.frameRate instead.", false)] |
| public float fps |
| { |
| get |
| { |
| return (float)m_Framerate; |
| } |
| set |
| { |
| m_Framerate = Mathf.Clamp(value, (float)kMinFrameRate, (float)kMaxFrameRate); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public double frameRate |
| { |
| get { return m_Framerate; } |
| set { m_Framerate = GetValidFrameRate(value); } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public void SetStandardFrameRate(StandardFrameRates enumValue) |
| { |
| FrameRate rate = TimeUtility.ToFrameRate(enumValue); |
| if (rate.IsValid()) |
| throw new ArgumentException(String.Format("StandardFrameRates {0}, is not defined", |
| enumValue.ToString())); |
| m_Framerate = rate.rate; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public bool scenePreview |
| { |
| get => m_ScenePreview; |
| set => m_ScenePreview = value; |
| } |
| } |
|
|
| [HideInInspector, SerializeField] List<ScriptableObject> m_Tracks; |
| [HideInInspector, SerializeField] double m_FixedDuration; |
| [HideInInspector, NonSerialized] TrackAsset[] m_CacheOutputTracks; |
| [HideInInspector, NonSerialized] List<TrackAsset> m_CacheRootTracks; |
| [HideInInspector, NonSerialized] TrackAsset[] m_CacheFlattenedTracks; |
| [HideInInspector, SerializeField] EditorSettings m_EditorSettings = new EditorSettings(); |
| [SerializeField] DurationMode m_DurationMode; |
|
|
| [HideInInspector, SerializeField] MarkerTrack m_MarkerTrack; |
|
|
| |
| |
| |
| public EditorSettings editorSettings |
| { |
| get { return m_EditorSettings; } |
| } |
|
|
| |
| |
| |
| public override double duration |
| { |
| get |
| { |
| |
| if (m_DurationMode == DurationMode.BasedOnClips) |
| { |
| |
| var discreteDuration = CalculateItemsDuration(); |
| if (discreteDuration <= 0) |
| return 0.0; |
| return (double)discreteDuration.OneTickBefore(); |
| } |
|
|
| return m_FixedDuration; |
| } |
| } |
|
|
| |
| |
| |
| public double fixedDuration |
| { |
| get |
| { |
| DiscreteTime discreteDuration = (DiscreteTime)m_FixedDuration; |
| if (discreteDuration <= 0) |
| return 0.0; |
|
|
| |
| return (double)discreteDuration.OneTickBefore(); |
| } |
| set { m_FixedDuration = Math.Max(0.0, value); } |
| } |
|
|
| |
| |
| |
| public DurationMode durationMode |
| { |
| get { return m_DurationMode; } |
| set { m_DurationMode = value; } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public override IEnumerable<PlayableBinding> outputs |
| { |
| get |
| { |
| foreach (var outputTracks in GetOutputTracks()) |
| foreach (var output in outputTracks.outputs) |
| yield return output; |
| } |
| } |
|
|
| |
| |
| |
| public ClipCaps clipCaps |
| { |
| get |
| { |
| var caps = ClipCaps.All; |
| foreach (var track in GetRootTracks()) |
| { |
| foreach (var clip in track.clips) |
| caps &= clip.clipCaps; |
| } |
| return caps; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public int outputTrackCount |
| { |
| get |
| { |
| UpdateOutputTrackCache(); |
| return m_CacheOutputTracks.Length; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public int rootTrackCount |
| { |
| get |
| { |
| UpdateRootTrackCache(); |
| return m_CacheRootTracks.Count; |
| } |
| } |
|
|
| void OnValidate() |
| { |
| editorSettings.frameRate = GetValidFrameRate(editorSettings.frameRate); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public TrackAsset GetRootTrack(int index) |
| { |
| UpdateRootTrackCache(); |
| return m_CacheRootTracks[index]; |
| } |
|
|
| |
| |
| |
| |
| |
| public IEnumerable<TrackAsset> GetRootTracks() |
| { |
| UpdateRootTrackCache(); |
| return m_CacheRootTracks; |
| } |
|
|
| |
| |
| |
| |
| |
| public TrackAsset GetOutputTrack(int index) |
| { |
| UpdateOutputTrackCache(); |
| return m_CacheOutputTracks[index]; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public IEnumerable<TrackAsset> GetOutputTracks() |
| { |
| UpdateOutputTrackCache(); |
| return m_CacheOutputTracks; |
| } |
|
|
| static double GetValidFrameRate(double frameRate) |
| { |
| return Math.Min(Math.Max(frameRate, EditorSettings.kMinFrameRate), EditorSettings.kMaxFrameRate); |
| } |
|
|
| void UpdateRootTrackCache() |
| { |
| if (m_CacheRootTracks == null) |
| { |
| if (m_Tracks == null) |
| m_CacheRootTracks = new List<TrackAsset>(); |
| else |
| { |
| m_CacheRootTracks = new List<TrackAsset>(m_Tracks.Count); |
| if (markerTrack != null) |
| { |
| m_CacheRootTracks.Add(markerTrack); |
| } |
|
|
| foreach (var t in m_Tracks) |
| { |
| var trackAsset = t as TrackAsset; |
| if (trackAsset != null) |
| m_CacheRootTracks.Add(trackAsset); |
| } |
| } |
| } |
| } |
|
|
| void UpdateOutputTrackCache() |
| { |
| if (m_CacheOutputTracks == null) |
| { |
| var outputTracks = new List<TrackAsset>(); |
| foreach (var flattenedTrack in flattenedTracks) |
| { |
| if (flattenedTrack != null && flattenedTrack.GetType() != typeof(GroupTrack) && !flattenedTrack.isSubTrack) |
| outputTracks.Add(flattenedTrack); |
| } |
| m_CacheOutputTracks = outputTracks.ToArray(); |
| } |
| } |
|
|
| internal TrackAsset[] flattenedTracks |
| { |
| get |
| { |
| if (m_CacheFlattenedTracks == null) |
| { |
| var list = new List<TrackAsset>(m_Tracks.Count * 2); |
| UpdateRootTrackCache(); |
|
|
| list.AddRange(m_CacheRootTracks); |
| for (int i = 0; i < m_CacheRootTracks.Count; i++) |
| { |
| AddSubTracksRecursive(m_CacheRootTracks[i], ref list); |
| } |
|
|
| m_CacheFlattenedTracks = list.ToArray(); |
| } |
| return m_CacheFlattenedTracks; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public MarkerTrack markerTrack |
| { |
| get { return m_MarkerTrack; } |
| } |
|
|
| |
| internal List<ScriptableObject> trackObjects |
| { |
| get { return m_Tracks; } |
| } |
|
|
| internal void AddTrackInternal(TrackAsset track) |
| { |
| m_Tracks.Add(track); |
| track.parent = this; |
| Invalidate(); |
| } |
|
|
| internal void RemoveTrack(TrackAsset track) |
| { |
| m_Tracks.Remove(track); |
| Invalidate(); |
| var parentTrack = track.parent as TrackAsset; |
| if (parentTrack != null) |
| { |
| parentTrack.RemoveSubTrack(track); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public override Playable CreatePlayable(PlayableGraph graph, GameObject go) |
| { |
| bool autoRebalanceTree = false; |
| #if UNITY_EDITOR |
| autoRebalanceTree = true; |
| #endif |
|
|
| |
| bool createOutputs = graph.GetPlayableCount() == 0; |
| var timeline = TimelinePlayable.Create(graph, GetOutputTracks(), go, autoRebalanceTree, createOutputs); |
| timeline.SetDuration(this.duration); |
| timeline.SetPropagateSetTime(true); |
| return timeline.IsValid() ? timeline : Playable.Null; |
| } |
|
|
| |
| |
| |
| void ISerializationCallbackReceiver.OnBeforeSerialize() |
| { |
| m_Version = k_LatestVersion; |
| } |
|
|
| |
| |
| |
| void ISerializationCallbackReceiver.OnAfterDeserialize() |
| { |
| |
| Invalidate(); |
| if (m_Version < k_LatestVersion) |
| { |
| UpgradeToLatestVersion(); |
| } |
| } |
|
|
| #if UNITY_EDITOR |
| internal event Action AssetModifiedOnDisk; |
| #endif |
| void __internalAwake() |
| { |
| if (m_Tracks == null) |
| m_Tracks = new List<ScriptableObject>(); |
|
|
| #if UNITY_EDITOR |
| |
| |
| if (!UnityEditor.EditorUtility.IsPersistent(this)) |
| return; |
| #endif |
|
|
| |
| for (int i = m_Tracks.Count - 1; i >= 0; i--) |
| { |
| TrackAsset asset = m_Tracks[i] as TrackAsset; |
| if (asset != null) |
| asset.parent = this; |
| #if UNITY_EDITOR |
| object o = m_Tracks[i]; |
| if (o == null) |
| { |
| Debug.LogWarning("Empty track found while loading timeline. It will be removed."); |
| m_Tracks.RemoveAt(i); |
| } |
| #endif |
| } |
|
|
| #if UNITY_EDITOR |
| AssetModifiedOnDisk?.Invoke(); |
| #endif |
| } |
|
|
| |
| |
| |
| |
| |
| public void GatherProperties(PlayableDirector director, IPropertyCollector driver) |
| { |
| var outputTracks = GetOutputTracks(); |
| foreach (var track in outputTracks) |
| { |
| if (!track.mutedInHierarchy) |
| track.GatherProperties(director, driver); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public void CreateMarkerTrack() |
| { |
| if (m_MarkerTrack == null) |
| { |
| m_MarkerTrack = CreateInstance<MarkerTrack>(); |
| TimelineCreateUtilities.SaveAssetIntoObject(m_MarkerTrack, this); |
| m_MarkerTrack.parent = this; |
| m_MarkerTrack.name = "Markers"; |
| Invalidate(); |
| } |
| } |
|
|
| |
| internal void Invalidate() |
| { |
| m_CacheRootTracks = null; |
| m_CacheOutputTracks = null; |
| m_CacheFlattenedTracks = null; |
| } |
|
|
| internal void UpdateFixedDurationWithItemsDuration() |
| { |
| m_FixedDuration = (double)CalculateItemsDuration(); |
| } |
|
|
| DiscreteTime CalculateItemsDuration() |
| { |
| var discreteDuration = new DiscreteTime(0); |
| foreach (var track in flattenedTracks) |
| { |
| if (track.muted) |
| continue; |
|
|
| discreteDuration = DiscreteTime.Max(discreteDuration, (DiscreteTime)track.end); |
| } |
|
|
| if (discreteDuration <= 0) |
| return new DiscreteTime(0); |
|
|
| return discreteDuration; |
| } |
|
|
| static void AddSubTracksRecursive(TrackAsset track, ref List<TrackAsset> allTracks) |
| { |
| if (track == null) |
| return; |
|
|
| allTracks.AddRange(track.GetChildTracks()); |
| foreach (TrackAsset subTrack in track.GetChildTracks()) |
| { |
| AddSubTracksRecursive(subTrack, ref allTracks); |
| } |
| } |
| } |
| } |
|
|