A game about forced loneliness, made by TACStudios
at master 560 lines 21 kB view raw
1using System; 2using System.Collections.Generic; 3using UnityEngine.Playables; 4 5namespace UnityEngine.Timeline 6{ 7 /// <summary> 8 /// A PlayableAsset that represents a timeline. 9 /// </summary> 10 [ExcludeFromPreset] 11 [Serializable] 12 [TimelineHelpURL(typeof(TimelineAsset))] 13 public partial class TimelineAsset : PlayableAsset, ISerializationCallbackReceiver, ITimelineClipAsset, IPropertyPreview 14 { 15 /// <summary> 16 /// How the duration of the timeline is determined. 17 /// </summary> 18 public enum DurationMode 19 { 20 /// <summary> 21 /// The duration of the timeline is determined based on the clips present. 22 /// </summary> 23 BasedOnClips, 24 /// <summary> 25 /// The duration of the timeline is a fixed length. 26 /// </summary> 27 FixedLength 28 } 29 30 /// <summary> 31 /// Properties of the timeline that are used by the editor 32 /// </summary> 33 [Serializable] 34 public class EditorSettings 35 { 36 internal static readonly double kMinFrameRate = TimeUtility.kFrameRateEpsilon; 37 internal static readonly double kMaxFrameRate = 1000.0; 38 internal static readonly double kDefaultFrameRate = 60.0; 39 [HideInInspector, SerializeField, FrameRateField] double m_Framerate = kDefaultFrameRate; 40 [HideInInspector, SerializeField] bool m_ScenePreview = true; 41 42 /// <summary> 43 /// The frames per second used for snapping and time ruler display 44 /// </summary> 45 [Obsolete("EditorSettings.fps has been deprecated. Use editorSettings.frameRate instead.", false)] 46 public float fps 47 { 48 get 49 { 50 return (float)m_Framerate; 51 } 52 set 53 { 54 m_Framerate = Mathf.Clamp(value, (float)kMinFrameRate, (float)kMaxFrameRate); 55 } 56 } 57 58 /// <summary> 59 /// The frames per second used for framelocked preview, frame snapping and time ruler display, 60 /// </summary> 61 /// <remarks> 62 /// If frameRate is set to a non-standard custom frame rate, Timeline playback 63 /// may give incorrect results when playbackLockedToFrame is true. 64 /// </remarks> 65 /// <seealso cref="UnityEngine.Timeline.TimelineAsset"/> 66 public double frameRate 67 { 68 get { return m_Framerate; } 69 set { m_Framerate = GetValidFrameRate(value); } 70 } 71 72 /// <summary> 73 /// Sets the EditorSetting frameRate to one of the provided standard frame rates. 74 /// </summary> 75 /// <param name="enumValue"> StandardFrameRates value, used to set the current EditorSettings frameRate value.</param> 76 /// <remarks> 77 /// When specifying drop frame values, it is recommended to select one of the provided standard frame rates. 78 /// Specifying a non-standard custom frame rate may give incorrect results when playbackLockedToFrame 79 /// is enabled during Timeline playback. 80 /// </remarks> 81 /// <exception cref="ArgumentException">Thrown when the enumValue is not a valid member of StandardFrameRates.</exception> 82 /// <seealso cref="UnityEngine.Timeline.TimelineAsset"/> 83 public void SetStandardFrameRate(StandardFrameRates enumValue) 84 { 85 FrameRate rate = TimeUtility.ToFrameRate(enumValue); 86 if (rate.IsValid()) 87 throw new ArgumentException(String.Format("StandardFrameRates {0}, is not defined", 88 enumValue.ToString())); 89 m_Framerate = rate.rate; 90 } 91 92 /// <summary> 93 /// Set to false to ignore scene preview when this timeline is played by the Timeline window. 94 /// </summary> 95 /// <remarks> 96 /// When set to false, this setting will 97 /// - Disable scene preview when this timeline is played by the Timeline window. 98 /// - Disable recording for all recordable tracks. 99 /// - Disable play range in the Timeline window. 100 /// - `Stop()` is not called on the `PlayableDirector` when switching between different `TimelineAsset`s in the TimelineWindow. 101 /// 102 /// `scenePreview` will only be applied if the asset is the master timeline. 103 /// </remarks> 104 /// <seealso cref="UnityEngine.Timeline.TimelineAsset"/> 105 public bool scenePreview 106 { 107 get => m_ScenePreview; 108 set => m_ScenePreview = value; 109 } 110 } 111 112 [HideInInspector, SerializeField] List<ScriptableObject> m_Tracks; 113 [HideInInspector, SerializeField] double m_FixedDuration; // only applied if duration mode is Fixed 114 [HideInInspector, NonSerialized] TrackAsset[] m_CacheOutputTracks; 115 [HideInInspector, NonSerialized] List<TrackAsset> m_CacheRootTracks; 116 [HideInInspector, NonSerialized] TrackAsset[] m_CacheFlattenedTracks; 117 [HideInInspector, SerializeField] EditorSettings m_EditorSettings = new EditorSettings(); 118 [SerializeField] DurationMode m_DurationMode; 119 120 [HideInInspector, SerializeField] MarkerTrack m_MarkerTrack; 121 122 /// <summary> 123 /// Settings used by timeline for editing purposes 124 /// </summary> 125 public EditorSettings editorSettings 126 { 127 get { return m_EditorSettings; } 128 } 129 130 /// <summary> 131 /// The length, in seconds, of the timeline 132 /// </summary> 133 public override double duration 134 { 135 get 136 { 137 // @todo cache this value when rebuilt 138 if (m_DurationMode == DurationMode.BasedOnClips) 139 { 140 //avoid having no clip evaluated at the end by removing a tick from the total duration 141 var discreteDuration = CalculateItemsDuration(); 142 if (discreteDuration <= 0) 143 return 0.0; 144 return (double)discreteDuration.OneTickBefore(); 145 } 146 147 return m_FixedDuration; 148 } 149 } 150 151 /// <summary> 152 /// The length of the timeline when durationMode is set to fixed length. 153 /// </summary> 154 public double fixedDuration 155 { 156 get 157 { 158 DiscreteTime discreteDuration = (DiscreteTime)m_FixedDuration; 159 if (discreteDuration <= 0) 160 return 0.0; 161 162 //avoid having no clip evaluated at the end by removing a tick from the total duration 163 return (double)discreteDuration.OneTickBefore(); 164 } 165 set { m_FixedDuration = Math.Max(0.0, value); } 166 } 167 168 /// <summary> 169 /// The mode used to determine the duration of the Timeline 170 /// </summary> 171 public DurationMode durationMode 172 { 173 get { return m_DurationMode; } 174 set { m_DurationMode = value; } 175 } 176 177 /// <summary> 178 /// A description of the PlayableOutputs that will be created by the timeline when instantiated. 179 /// </summary> 180 /// <remarks> 181 /// Each track will create an PlayableOutput 182 /// </remarks> 183 public override IEnumerable<PlayableBinding> outputs 184 { 185 get 186 { 187 foreach (var outputTracks in GetOutputTracks()) 188 foreach (var output in outputTracks.outputs) 189 yield return output; 190 } 191 } 192 193 /// <summary> 194 /// The capabilities supported by all clips in the timeline. 195 /// </summary> 196 public ClipCaps clipCaps 197 { 198 get 199 { 200 var caps = ClipCaps.All; 201 foreach (var track in GetRootTracks()) 202 { 203 foreach (var clip in track.clips) 204 caps &= clip.clipCaps; 205 } 206 return caps; 207 } 208 } 209 210 /// <summary> 211 /// Returns the the number of output tracks in the Timeline. 212 /// </summary> 213 /// <remarks> 214 /// 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. 215 /// </remarks> 216 public int outputTrackCount 217 { 218 get 219 { 220 UpdateOutputTrackCache(); // updates the cache if necessary 221 return m_CacheOutputTracks.Length; 222 } 223 } 224 225 /// <summary> 226 /// Returns the number of tracks at the root level of the timeline. 227 /// </summary> 228 /// <remarks> 229 /// 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 230 /// </remarks> 231 public int rootTrackCount 232 { 233 get 234 { 235 UpdateRootTrackCache(); 236 return m_CacheRootTracks.Count; 237 } 238 } 239 240 void OnValidate() 241 { 242 editorSettings.frameRate = GetValidFrameRate(editorSettings.frameRate); 243 } 244 245 /// <summary> 246 /// Retrieves at root track at the specified index. 247 /// </summary> 248 /// <param name="index">Index of the root track to get. Must be between 0 and rootTrackCount</param> 249 /// <remarks> 250 /// 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. 251 /// </remarks> 252 /// <returns>Root track at the specified index.</returns> 253 public TrackAsset GetRootTrack(int index) 254 { 255 UpdateRootTrackCache(); 256 return m_CacheRootTracks[index]; 257 } 258 259 /// <summary> 260 /// Get an enumerable list of all root tracks. 261 /// </summary> 262 /// <returns>An IEnumerable of all root tracks.</returns> 263 /// <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> 264 public IEnumerable<TrackAsset> GetRootTracks() 265 { 266 UpdateRootTrackCache(); 267 return m_CacheRootTracks; 268 } 269 270 /// <summary> 271 /// Retrives the output track from the given index. 272 /// </summary> 273 /// <param name="index">Index of the output track to retrieve. Must be between 0 and outputTrackCount</param> 274 /// <returns>The output track from the given index</returns> 275 public TrackAsset GetOutputTrack(int index) 276 { 277 UpdateOutputTrackCache(); 278 return m_CacheOutputTracks[index]; 279 } 280 281 /// <summary> 282 /// Gets a list of all output tracks in the Timeline. 283 /// </summary> 284 /// <returns>An IEnumerable of all output tracks</returns> 285 /// <remarks> 286 /// 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. 287 /// </remarks> 288 public IEnumerable<TrackAsset> GetOutputTracks() 289 { 290 UpdateOutputTrackCache(); 291 return m_CacheOutputTracks; 292 } 293 294 static double GetValidFrameRate(double frameRate) 295 { 296 return Math.Min(Math.Max(frameRate, EditorSettings.kMinFrameRate), EditorSettings.kMaxFrameRate); 297 } 298 299 void UpdateRootTrackCache() 300 { 301 if (m_CacheRootTracks == null) 302 { 303 if (m_Tracks == null) 304 m_CacheRootTracks = new List<TrackAsset>(); 305 else 306 { 307 m_CacheRootTracks = new List<TrackAsset>(m_Tracks.Count); 308 if (markerTrack != null) 309 { 310 m_CacheRootTracks.Add(markerTrack); 311 } 312 313 foreach (var t in m_Tracks) 314 { 315 var trackAsset = t as TrackAsset; 316 if (trackAsset != null) 317 m_CacheRootTracks.Add(trackAsset); 318 } 319 } 320 } 321 } 322 323 void UpdateOutputTrackCache() 324 { 325 if (m_CacheOutputTracks == null) 326 { 327 var outputTracks = new List<TrackAsset>(); 328 foreach (var flattenedTrack in flattenedTracks) 329 { 330 if (flattenedTrack != null && flattenedTrack.GetType() != typeof(GroupTrack) && !flattenedTrack.isSubTrack) 331 outputTracks.Add(flattenedTrack); 332 } 333 m_CacheOutputTracks = outputTracks.ToArray(); 334 } 335 } 336 337 internal TrackAsset[] flattenedTracks 338 { 339 get 340 { 341 if (m_CacheFlattenedTracks == null) 342 { 343 var list = new List<TrackAsset>(m_Tracks.Count * 2); 344 UpdateRootTrackCache(); 345 346 list.AddRange(m_CacheRootTracks); 347 for (int i = 0; i < m_CacheRootTracks.Count; i++) 348 { 349 AddSubTracksRecursive(m_CacheRootTracks[i], ref list); 350 } 351 352 m_CacheFlattenedTracks = list.ToArray(); 353 } 354 return m_CacheFlattenedTracks; 355 } 356 } 357 358 /// <summary> 359 /// Gets the marker track for this TimelineAsset. 360 /// </summary> 361 /// <returns>Returns the marker track.</returns> 362 /// <remarks> 363 /// Use <see cref="TrackAsset.GetMarkers"/> to get a list of the markers on the returned track. 364 /// </remarks> 365 public MarkerTrack markerTrack 366 { 367 get { return m_MarkerTrack; } 368 } 369 370 // access to the track list as scriptable object 371 internal List<ScriptableObject> trackObjects 372 { 373 get { return m_Tracks; } 374 } 375 376 internal void AddTrackInternal(TrackAsset track) 377 { 378 m_Tracks.Add(track); 379 track.parent = this; 380 Invalidate(); 381 } 382 383 internal void RemoveTrack(TrackAsset track) 384 { 385 m_Tracks.Remove(track); 386 Invalidate(); 387 var parentTrack = track.parent as TrackAsset; 388 if (parentTrack != null) 389 { 390 parentTrack.RemoveSubTrack(track); 391 } 392 } 393 394 /// <summary> 395 /// Creates an instance of the timeline 396 /// </summary> 397 /// <param name="graph">PlayableGraph that will own the playable</param> 398 /// <param name="go">The gameobject that triggered the graph build</param> 399 /// <returns>The Root Playable of the Timeline</returns> 400 public override Playable CreatePlayable(PlayableGraph graph, GameObject go) 401 { 402 bool autoRebalanceTree = false; 403#if UNITY_EDITOR 404 autoRebalanceTree = true; 405#endif 406 407 // only create outputs if we are not nested 408 bool createOutputs = graph.GetPlayableCount() == 0; 409 var timeline = TimelinePlayable.Create(graph, GetOutputTracks(), go, autoRebalanceTree, createOutputs); 410 timeline.SetDuration(this.duration); 411 timeline.SetPropagateSetTime(true); 412 return timeline.IsValid() ? timeline : Playable.Null; 413 } 414 415 /// <summary> 416 /// Called before Unity serializes this object. 417 /// </summary> 418 void ISerializationCallbackReceiver.OnBeforeSerialize() 419 { 420 m_Version = k_LatestVersion; 421 } 422 423 /// <summary> 424 /// Called after Unity deserializes this object. 425 /// </summary> 426 void ISerializationCallbackReceiver.OnAfterDeserialize() 427 { 428 // resets cache on an Undo 429 Invalidate(); // resets cache on an Undo 430 if (m_Version < k_LatestVersion) 431 { 432 UpgradeToLatestVersion(); 433 } 434 } 435 436#if UNITY_EDITOR 437 internal event Action AssetModifiedOnDisk; 438#endif 439 void __internalAwake() 440 { 441 if (m_Tracks == null) 442 m_Tracks = new List<ScriptableObject>(); 443 444#if UNITY_EDITOR 445 // case 1280331 -- embedding the timeline asset inside a prefab will create a temporary non-persistent version of an asset 446 // setting the track parents to this will change persistent tracks 447 if (!UnityEditor.EditorUtility.IsPersistent(this)) 448 return; 449#endif 450 451 // validate the array. DON'T remove Unity null objects, just actual null objects 452 for (int i = m_Tracks.Count - 1; i >= 0; i--) 453 { 454 TrackAsset asset = m_Tracks[i] as TrackAsset; 455 if (asset != null) 456 asset.parent = this; 457#if UNITY_EDITOR 458 object o = m_Tracks[i]; 459 if (o == null) 460 { 461 Debug.LogWarning("Empty track found while loading timeline. It will be removed."); 462 m_Tracks.RemoveAt(i); 463 } 464#endif 465 } 466 467#if UNITY_EDITOR 468 AssetModifiedOnDisk?.Invoke(); 469#endif 470 } 471 472 /// <summary> 473 /// Called by the Timeline Editor to gather properties requiring preview. 474 /// </summary> 475 /// <param name="director">The PlayableDirector invoking the preview</param> 476 /// <param name="driver">PropertyCollector used to gather previewable properties</param> 477 public void GatherProperties(PlayableDirector director, IPropertyCollector driver) 478 { 479 var outputTracks = GetOutputTracks(); 480 foreach (var track in outputTracks) 481 { 482 if (!track.mutedInHierarchy) 483 track.GatherProperties(director, driver); 484 } 485 } 486 487 /// <summary> 488 /// Creates a marker track for the TimelineAsset. 489 /// </summary> 490 /// In the editor, the marker track appears under the Timeline ruler. 491 /// <remarks> 492 /// This track is always bound to the GameObject that contains the PlayableDirector component for the current timeline. 493 /// The marker track is created the first time this method is called. If the marker track is already created, this method does nothing. 494 /// </remarks> 495 public void CreateMarkerTrack() 496 { 497 if (m_MarkerTrack == null) 498 { 499 m_MarkerTrack = CreateInstance<MarkerTrack>(); 500 TimelineCreateUtilities.SaveAssetIntoObject(m_MarkerTrack, this); 501 m_MarkerTrack.parent = this; 502 m_MarkerTrack.name = "Markers"; // This name will show up in the bindings list if it contains signals 503 Invalidate(); 504 } 505 } 506 507 internal void RemoveMarkerTrack() 508 { 509 if (m_MarkerTrack != null) 510 { 511 MarkerTrack markerTrack = m_MarkerTrack; 512 m_MarkerTrack = null; 513 TimelineCreateUtilities.RemoveAssetFromObject(markerTrack, this); 514 Invalidate(); 515 } 516 } 517 518 // Invalidates the asset, call this if changing the asset data 519 internal void Invalidate() 520 { 521 m_CacheRootTracks = null; 522 m_CacheOutputTracks = null; 523 m_CacheFlattenedTracks = null; 524 } 525 526 internal void UpdateFixedDurationWithItemsDuration() 527 { 528 m_FixedDuration = (double)CalculateItemsDuration(); 529 } 530 531 DiscreteTime CalculateItemsDuration() 532 { 533 var discreteDuration = new DiscreteTime(0); 534 foreach (var track in flattenedTracks) 535 { 536 if (track.muted) 537 continue; 538 539 discreteDuration = DiscreteTime.Max(discreteDuration, (DiscreteTime)track.end); 540 } 541 542 if (discreteDuration <= 0) 543 return new DiscreteTime(0); 544 545 return discreteDuration; 546 } 547 548 static void AddSubTracksRecursive(TrackAsset track, ref List<TrackAsset> allTracks) 549 { 550 if (track == null) 551 return; 552 553 allTracks.AddRange(track.GetChildTracks()); 554 foreach (TrackAsset subTrack in track.GetChildTracks()) 555 { 556 AddSubTracksRecursive(subTrack, ref allTracks); 557 } 558 } 559 } 560}