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.
549 lines
20 KiB
549 lines
20 KiB
using System; |
|
using System.Collections.Generic; |
|
using UnityEngine.Playables; |
|
|
|
namespace UnityEngine.Timeline |
|
{ |
|
/// <summary> |
|
/// A PlayableAsset that represents a timeline. |
|
/// </summary> |
|
[ExcludeFromPreset] |
|
[Serializable] |
|
[TimelineHelpURL(typeof(TimelineAsset))] |
|
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 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; |
|
|
|
/// <summary> |
|
/// The frames per second used for snapping and time ruler display |
|
/// </summary> |
|
[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); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The frames per second used for framelocked preview, frame snapping and time ruler display, |
|
/// </summary> |
|
/// <remarks> |
|
/// If frameRate is set to a non-standard custom frame rate, Timeline playback |
|
/// may give incorrect results when playbackLockedToFrame is true. |
|
/// </remarks> |
|
/// <seealso cref="UnityEngine.Timeline.TimelineAsset"/> |
|
public double frameRate |
|
{ |
|
get { return m_Framerate; } |
|
set { m_Framerate = GetValidFrameRate(value); } |
|
} |
|
|
|
/// <summary> |
|
/// Sets the EditorSetting frameRate to one of the provided standard frame rates. |
|
/// </summary> |
|
/// <param name="enumValue"> StandardFrameRates value, used to set the current EditorSettings frameRate value.</param> |
|
/// <remarks> |
|
/// When specifying drop frame values, it is recommended to select one of the provided standard frame rates. |
|
/// Specifying a non-standard custom frame rate may give incorrect results when playbackLockedToFrame |
|
/// is enabled during Timeline playback. |
|
/// </remarks> |
|
/// <exception cref="ArgumentException">Thrown when the enumValue is not a valid member of StandardFrameRates.</exception> |
|
/// <seealso cref="UnityEngine.Timeline.TimelineAsset"/> |
|
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; |
|
} |
|
|
|
/// <summary> |
|
/// Set to false to ignore scene preview when this timeline is played by the Timeline window. |
|
/// </summary> |
|
/// <remarks> |
|
/// When set to false, this setting will |
|
/// - Disable scene preview when this timeline is played by the Timeline window. |
|
/// - Disable recording for all recordable tracks. |
|
/// - Disable play range in the Timeline window. |
|
/// - `Stop()` is not called on the `PlayableDirector` when switching between different `TimelineAsset`s in the TimelineWindow. |
|
/// |
|
/// `scenePreview` will only be applied if the asset is the master timeline. |
|
/// </remarks> |
|
/// <seealso cref="UnityEngine.Timeline.TimelineAsset"/> |
|
public bool scenePreview |
|
{ |
|
get => m_ScenePreview; |
|
set => m_ScenePreview = 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] 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) |
|
{ |
|
//avoid having no clip evaluated at the end by removing a tick from the total duration |
|
var discreteDuration = CalculateItemsDuration(); |
|
if (discreteDuration <= 0) |
|
return 0.0; |
|
return (double)discreteDuration.OneTickBefore(); |
|
} |
|
|
|
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; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The capabilities supported by all clips in the timeline. |
|
/// </summary> |
|
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.frameRate = GetValidFrameRate(editorSettings.frameRate); |
|
} |
|
|
|
/// <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> |
|
/// <returns>Root track at the specified index.</returns> |
|
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; |
|
} |
|
|
|
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; |
|
} |
|
} |
|
|
|
/// <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.SetDuration(this.duration); |
|
timeline.SetPropagateSetTime(true); |
|
return timeline.IsValid() ? timeline : Playable.Null; |
|
} |
|
|
|
/// <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() |
|
{ |
|
// resets cache on an Undo |
|
Invalidate(); // resets cache on an Undo |
|
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 |
|
// case 1280331 -- embedding the timeline asset inside a prefab will create a temporary non-persistent version of an asset |
|
// setting the track parents to this will change persistent tracks |
|
if (!UnityEditor.EditorUtility.IsPersistent(this)) |
|
return; |
|
#endif |
|
|
|
// 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 |
|
} |
|
|
|
#if UNITY_EDITOR |
|
AssetModifiedOnDisk?.Invoke(); |
|
#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) |
|
{ |
|
if (!track.mutedInHierarchy) |
|
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; |
|
} |
|
|
|
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); |
|
} |
|
} |
|
} |
|
}
|
|
|