using System; using System.Collections.Generic; using UnityEngine; namespace CinemaDirector { public abstract class TimelineTrack : MonoBehaviour, IOptimizable { [SerializeField] private int ordinal = -1; // Ordering of Tracks [SerializeField] private bool canOptimize = true; // If true, this Track will load all items into cache on Optimize(). // Options for when this Track will execute its Timeline Items. public PlaybackMode PlaybackMode = PlaybackMode.RuntimeAndEdit; protected float elapsedTime; // A cache of the TimelineItems for optimization purposes. protected TimelineItem[] itemCache; // A list of the cutscene item types that this Track is allowed to contain. protected List allowedItemTypes; private bool hasBeenOptimized = false; /// /// Prepares the TimelineTrack by caching all TimelineItems contained inside of it. /// public virtual void Optimize() { if (canOptimize) { itemCache = GetTimelineItems(); } foreach (TimelineItem item in GetTimelineItems()) { if (item is IOptimizable) { (item as IOptimizable).Optimize(); } } hasBeenOptimized = true; } /// /// Perform any initialization before the cutscene begins a fresh playback /// public virtual void Initialize() { elapsedTime = -1f; foreach (TimelineItem item in GetTimelineItems()) { item.Initialize(); } } /// /// Update the track to the given time /// /// public virtual void UpdateTrack(float runningTime, float deltaTime) { float previousTime = elapsedTime; elapsedTime = runningTime; foreach (TimelineItem item in GetTimelineItems()) { CinemaGlobalEvent cinemaEvent = item as CinemaGlobalEvent; if (cinemaEvent == null) continue; if ((previousTime < cinemaEvent.Firetime) && (((elapsedTime >= cinemaEvent.Firetime)))) { cinemaEvent.Trigger(); } else if (((previousTime >= cinemaEvent.Firetime) && (elapsedTime < cinemaEvent.Firetime))) { cinemaEvent.Reverse(); } } foreach (TimelineItem item in GetTimelineItems()) { CinemaGlobalAction action = item as CinemaGlobalAction; if (action == null) continue; if ((previousTime < action.Firetime && elapsedTime >= action.Firetime) && elapsedTime < action.EndTime) { action.Trigger(); } else if ((previousTime < action.EndTime) && (elapsedTime >= action.EndTime)) { action.End(); } else if (previousTime > action.Firetime && previousTime <= action.EndTime && elapsedTime < action.Firetime) { action.ReverseTrigger(); } else if ((previousTime > (action.EndTime) && (elapsedTime > action.Firetime) && (elapsedTime <= action.EndTime))) { action.ReverseEnd(); } else if ((elapsedTime > action.Firetime) && (elapsedTime < action.EndTime)) { float t = runningTime - action.Firetime; action.UpdateTime(t, deltaTime); } } } /// /// Notify track items that the cutscene has been paused /// public virtual void Pause() { } /// /// Notify track items that the cutscene has been resumed from a paused state. /// public virtual void Resume() { } /// /// The cutscene has been set to an arbitrary time by the user. /// Processing must take place to catch up to the new time. /// /// The new cutscene running time public virtual void SetTime(float time) { float previousTime = elapsedTime; elapsedTime = time; foreach (TimelineItem item in GetTimelineItems()) { // Check if it is a global event. CinemaGlobalEvent cinemaEvent = item as CinemaGlobalEvent; if (cinemaEvent != null) { if ((previousTime < cinemaEvent.Firetime) && (((elapsedTime >= cinemaEvent.Firetime)))) { cinemaEvent.Trigger(); } else if (((previousTime >= cinemaEvent.Firetime) && (elapsedTime < cinemaEvent.Firetime))) { cinemaEvent.Reverse(); } } // Check if it is a global action. CinemaGlobalAction action = item as CinemaGlobalAction; if (action != null) { action.SetTime((time - action.Firetime), time - previousTime); } } } /// /// Retrieve a list of important times for this track within the given range. /// /// The starting point of the range. /// The end point of the range. /// A list of ordered milestone times within the given range. public virtual List GetMilestones(float from, float to) { bool isReverse = from > to; List times = new List(); foreach (TimelineItem item in GetTimelineItems()) { if ((!isReverse && from < item.Firetime && to >= item.Firetime) || (isReverse && from > item.Firetime && to <= item.Firetime)) { if (!times.Contains(item.Firetime)) { times.Add(item.Firetime); } } if (item is TimelineAction) { float endTime = (item as TimelineAction).EndTime; if ((!isReverse && from < endTime && to >= endTime) || (isReverse && from > endTime && to <= endTime)) { if (!times.Contains(endTime)) { times.Add(endTime); } } } } times.Sort(); return times; } /// /// Notify the track items that the cutscene has been stopped /// public virtual void Stop() { foreach (TimelineItem item in GetTimelineItems()) { item.Stop(); } } /// /// Returns all allowed Timeline Item types. /// /// A list of allowed cutscene item types. public List GetAllowedCutsceneItems() { if (allowedItemTypes == null) { allowedItemTypes = DirectorRuntimeHelper.GetAllowedItemTypes(this); } return allowedItemTypes; } /// /// The Cutscene that this Track is associated with. Can return null. /// public Cutscene Cutscene { get { return ((this.TrackGroup == null) ? null : this.TrackGroup.Cutscene); } } /// /// The TrackGroup associated with this Track. Can return null. /// public TrackGroup TrackGroup { get { TrackGroup group = null; if (transform.parent != null) { group = transform.parent.GetComponentInParent(); if (group == null) { Debug.LogError("No TrackGroup found on parent.", this); } } else { Debug.LogError("Track has no parent.", this); } return group; } } /// /// Ordinal for UI ranking. /// public int Ordinal { get { return ordinal; } set { ordinal = value; } } /// /// Enable this if the Track does not have Items added/removed during running. /// public bool CanOptimize { get { return canOptimize; } set { canOptimize = value; } } /// /// Get all TimelineItems that are allowed to be in this Track. /// /// A filtered list of Timeline Items. public TimelineItem[] GetTimelineItems() { // Return the cache if possible if (hasBeenOptimized) { return itemCache; } List items = new List(); foreach (Type t in GetAllowedCutsceneItems()) { var components = GetComponentsInChildren(t); foreach (var component in components) { items.Add((TimelineItem)component); } } return items.ToArray(); } public virtual TimelineItem[] TimelineItems { get { return base.GetComponentsInChildren(); } } } }