|
|
using System;using System.Collections.Generic;using UnityEngine.Playables;
namespace UnityEngine.Timeline{ /// <summary>
/// A PlayableAsset that represents a timeline.
/// </summary>
[ExcludeFromPreset] [Serializable] public partial class TimelineAsset : PlayableAsset, ISerializationCallbackReceiver, ITimelineClipAsset, IPropertyPreview { /// <summary>
/// How the duration of the timeline is determined.
/// </summary>
public enum DurationMode { /// <summary>
/// The duration of the timeline is determined based on the clips present.
/// </summary>
BasedOnClips, /// <summary>
/// The duration of the timeline is a fixed length.
/// </summary>
FixedLength }
/// <summary>
/// Properties of the timeline that are used by the editor
/// </summary>
[Serializable] public class EditorSettings { internal static readonly float kMinFps = (float)TimeUtility.kFrameRateEpsilon; internal static readonly float kMaxFps = 1000.0f; internal static readonly float kDefaultFps = 60.0f; [HideInInspector, SerializeField] float m_Framerate = kDefaultFps;
/// <summary>
/// The frames per second used for snapping and time ruler display
/// </summary>
public float fps { get { return m_Framerate; } set { m_Framerate = GetValidFramerate(value); } } }
[HideInInspector, SerializeField] List<ScriptableObject> m_Tracks; [HideInInspector, SerializeField] double m_FixedDuration; // only applied if duration mode is Fixed
[HideInInspector, NonSerialized] TrackAsset[] m_CacheOutputTracks; [HideInInspector, NonSerialized] List<TrackAsset> m_CacheRootTracks; [HideInInspector, NonSerialized] List<TrackAsset> m_CacheFlattenedTracks; [HideInInspector, SerializeField] EditorSettings m_EditorSettings = new EditorSettings(); [SerializeField] DurationMode m_DurationMode;
[HideInInspector, SerializeField] MarkerTrack m_MarkerTrack;
/// <summary>
/// Settings used by timeline for editing purposes
/// </summary>
public EditorSettings editorSettings { get { return m_EditorSettings; } }
/// <summary>
/// The length, in seconds, of the timeline
/// </summary>
public override double duration { get { // @todo cache this value when rebuilt
if (m_DurationMode == DurationMode.BasedOnClips) return CalculateDuration();
return m_FixedDuration; } }
/// <summary>
/// The length of the timeline when durationMode is set to fixed length.
/// </summary>
public double fixedDuration { get { DiscreteTime discreteDuration = (DiscreteTime)m_FixedDuration; if (discreteDuration <= 0) return 0.0;
//avoid having no clip evaluated at the end by removing a tick from the total duration
return (double)discreteDuration.OneTickBefore(); } set { m_FixedDuration = Math.Max(0.0, value); } }
/// <summary>
/// The mode used to determine the duration of the Timeline
/// </summary>
public DurationMode durationMode { get { return m_DurationMode; } set { m_DurationMode = value; } }
/// <summary>
/// A description of the PlayableOutputs that will be created by the timeline when instantiated.
/// </summary>
/// <remarks>
/// Each track will create an PlayableOutput
/// </remarks>
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; } }
/// <summary>
/// Returns the the number of output tracks in the Timeline.
/// </summary>
/// <remarks>
/// An output track is a track the generates a PlayableOutput. In general, an output track is any track that is not a GroupTrack, a subtrack, or override track.
/// </remarks>
public int outputTrackCount { get { UpdateOutputTrackCache(); // updates the cache if necessary
return m_CacheOutputTracks.Length; } }
/// <summary>
/// Returns the number of tracks at the root level of the timeline.
/// </summary>
/// <remarks>
/// A root track refers to all tracks that occur at the root of the timeline. These are the outmost level GroupTracks, and output tracks that do not belong to any group
/// </remarks>
public int rootTrackCount { get { UpdateRootTrackCache(); return m_CacheRootTracks.Count; } }
void OnValidate() { editorSettings.fps = GetValidFramerate(editorSettings.fps); }
static float GetValidFramerate(float framerate) { return Mathf.Clamp(framerate, EditorSettings.kMinFps, EditorSettings.kMaxFps); }
/// <summary>
/// Retrieves at root track at the specified index.
/// </summary>
/// <param name="index">Index of the root track to get. Must be between 0 and rootTrackCount</param>
/// <remarks>
/// A root track refers to all tracks that occur at the root of the timeline. These are the outmost level GroupTracks, and output tracks that do not belong to any group.
/// </remarks>
public TrackAsset GetRootTrack(int index) { UpdateRootTrackCache(); return m_CacheRootTracks[index]; }
/// <summary>
/// Get an enumerable list of all root tracks.
/// </summary>
/// <returns>An IEnumerable of all root tracks.</returns>
/// <remarks>A root track refers to all tracks that occur at the root of the timeline. These are the outmost level GroupTracks, and output tracks that do not belong to any group.</remarks>
public IEnumerable<TrackAsset> GetRootTracks() { UpdateRootTrackCache(); return m_CacheRootTracks; }
/// <summary>
/// Retrives the output track from the given index.
/// </summary>
/// <param name="index">Index of the output track to retrieve. Must be between 0 and outputTrackCount</param>
/// <returns>The output track from the given index</returns>
public TrackAsset GetOutputTrack(int index) { UpdateOutputTrackCache(); return m_CacheOutputTracks[index]; }
/// <summary>
/// Gets a list of all output tracks in the Timeline.
/// </summary>
/// <returns>An IEnumerable of all output tracks</returns>
/// <remarks>
/// An output track is a track the generates a PlayableOutput. In general, an output track is any track that is not a GroupTrack or subtrack.
/// </remarks>
public IEnumerable<TrackAsset> GetOutputTracks() { UpdateOutputTrackCache(); return m_CacheOutputTracks; }
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 IEnumerable<TrackAsset> flattenedTracks { get { if (m_CacheFlattenedTracks == null) { m_CacheFlattenedTracks = new List<TrackAsset>(m_Tracks.Count * 2); UpdateRootTrackCache();
m_CacheFlattenedTracks.AddRange(m_CacheRootTracks); for (int i = 0; i < m_CacheRootTracks.Count; i++) { AddSubTracksRecursive(m_CacheRootTracks[i], ref m_CacheFlattenedTracks); } } return m_CacheFlattenedTracks; } }
/// <summary>
/// Gets the marker track for this TimelineAsset.
/// </summary>
/// <returns>Returns the marker track.</returns>
/// <remarks>
/// Use <see cref="TrackAsset.GetMarkers"/> to get a list of the markers on the returned track.
/// </remarks>
public MarkerTrack markerTrack { get { return m_MarkerTrack; } }
// access to the track list as scriptable object
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); } }
/// <summary>
/// Creates an instance of the timeline
/// </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 Timeline</returns>
public override Playable CreatePlayable(PlayableGraph graph, GameObject go) { bool autoRebalanceTree = false; #if UNITY_EDITOR
autoRebalanceTree = true; #endif
// only create outputs if we are not nested
bool createOutputs = graph.GetPlayableCount() == 0; var timeline = TimelinePlayable.Create(graph, GetOutputTracks(), go, autoRebalanceTree, createOutputs); timeline.SetPropagateSetTime(true); return timeline.IsValid() ? timeline : Playable.Null; }
void ISerializationCallbackReceiver.OnBeforeSerialize() { m_Version = k_LatestVersion; }
// resets cache on an Undo
void ISerializationCallbackReceiver.OnAfterDeserialize() { Invalidate(); // resets cache on an Undo
if (m_Version < k_LatestVersion) { UpgradeToLatestVersion(); } }
void __internalAwake() { if (m_Tracks == null) m_Tracks = new List<ScriptableObject>();
// validate the array. DON'T remove Unity null objects, just actual null objects
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
} }
/// <summary>
/// Called by the Timeline Editor to gather properties requiring preview.
/// </summary>
/// <param name="director">The PlayableDirector invoking the preview</param>
/// <param name="driver">PropertyCollector used to gather previewable properties</param>
public void GatherProperties(PlayableDirector director, IPropertyCollector driver) { var outputTracks = GetOutputTracks(); foreach (var track in outputTracks) { track.GatherProperties(director, driver); } }
/// <summary>
/// Creates a marker track for the TimelineAsset.
/// </summary>
/// In the editor, the marker track appears under the Timeline ruler.
/// <remarks>
/// This track is always bound to the GameObject that contains the PlayableDirector component for the current timeline.
/// The marker track is created the first time this method is called. If the marker track is already created, this method does nothing.
/// </remarks>
public void CreateMarkerTrack() { if (m_MarkerTrack == null) { m_MarkerTrack = CreateInstance<MarkerTrack>(); TimelineCreateUtilities.SaveAssetIntoObject(m_MarkerTrack, this); m_MarkerTrack.parent = this; m_MarkerTrack.name = "Markers"; // This name will show up in the bindings list if it contains signals
Invalidate(); } }
// Invalidates the asset, call this if changing the asset data
internal void Invalidate() { m_CacheRootTracks = null; m_CacheOutputTracks = null; m_CacheFlattenedTracks = null; }
double CalculateDuration() { 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 0.0;
//avoid having no clip evaluated at the end by removing a tick from the total duration
return (double)discreteDuration.OneTickBefore(); }
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); } } }}
|