diff options
Diffstat (limited to 'Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Playables/TimeNotificationBehaviour.cs')
| -rw-r--r-- | Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Playables/TimeNotificationBehaviour.cs | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Playables/TimeNotificationBehaviour.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Playables/TimeNotificationBehaviour.cs new file mode 100644 index 0000000..66a31d1 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Playables/TimeNotificationBehaviour.cs @@ -0,0 +1,254 @@ +using System; +using System.Collections.Generic; +using UnityEngine.Playables; + +namespace UnityEngine.Timeline +{ + /// <summary> + /// Use this PlayableBehaviour to send notifications at a given time. + /// </summary> + /// <seealso cref="UnityEngine.Timeline.NotificationFlags"/> + public class TimeNotificationBehaviour : PlayableBehaviour + { + struct NotificationEntry + { + public double time; + public INotification payload; + public bool notificationFired; + public NotificationFlags flags; + + public bool triggerInEditor + { + get { return (flags & NotificationFlags.TriggerInEditMode) != 0; } + } + public bool prewarm + { + get { return (flags & NotificationFlags.Retroactive) != 0; } + } + public bool triggerOnce + { + get { return (flags & NotificationFlags.TriggerOnce) != 0; } + } + } + + readonly List<NotificationEntry> m_Notifications = new List<NotificationEntry>(); + double m_PreviousTime; + bool m_NeedSortNotifications; + + Playable m_TimeSource; + + /// <summary> + /// Sets an optional Playable that provides duration and Wrap mode information. + /// </summary> + /// <remarks> + /// timeSource is optional. By default, the duration and Wrap mode will come from the current Playable. + /// </remarks> + public Playable timeSource + { + set { m_TimeSource = value; } + } + + /// <summary> + /// Creates and initializes a ScriptPlayable with a TimeNotificationBehaviour. + /// </summary> + /// <param name="graph">The playable graph.</param> + /// <param name="duration">The duration of the playable.</param> + /// <param name="loopMode">The loop mode of the playable.</param> + /// <returns>A new TimeNotificationBehaviour linked to the PlayableGraph.</returns> + public static ScriptPlayable<TimeNotificationBehaviour> Create(PlayableGraph graph, double duration, DirectorWrapMode loopMode) + { + var notificationsPlayable = ScriptPlayable<TimeNotificationBehaviour>.Create(graph); + notificationsPlayable.SetDuration(duration); + notificationsPlayable.SetTimeWrapMode(loopMode); + notificationsPlayable.SetPropagateSetTime(true); + return notificationsPlayable; + } + + /// <summary> + /// Adds a notification to be sent with flags, at a specific time. + /// </summary> + /// <param name="time">The time to send the notification.</param> + /// <param name="payload">The notification.</param> + /// <param name="flags">The notification flags that determine the notification behaviour. This parameter is set to Retroactive by default.</param> + /// <seealso cref="UnityEngine.Timeline.NotificationFlags"/> + public void AddNotification(double time, INotification payload, NotificationFlags flags = NotificationFlags.Retroactive) + { + m_Notifications.Add(new NotificationEntry + { + time = time, + payload = payload, + flags = flags + }); + m_NeedSortNotifications = true; + } + + /// <summary> + /// This method is called when the PlayableGraph that owns this PlayableBehaviour starts. + /// </summary> + /// <param name="playable">The reference to the playable associated with this PlayableBehaviour.</param> + public override void OnGraphStart(Playable playable) + { + SortNotifications(); + for (var i = 0; i < m_Notifications.Count; i++) + { + var notification = m_Notifications[i]; + notification.notificationFired = false; + m_Notifications[i] = notification; + } + + m_PreviousTime = playable.GetTime(); + } + + /// <summary> + /// This method is called when the Playable play state is changed to PlayState.Paused + /// </summary> + /// <param name="playable">The reference to the playable associated with this PlayableBehaviour.</param> + /// <param name="info">Playable context information such as weight, evaluationType, and so on.</param> + public override void OnBehaviourPause(Playable playable, FrameData info) + { + if (playable.IsDone()) + { + SortNotifications(); + for (var i = 0; i < m_Notifications.Count; i++) + { + var e = m_Notifications[i]; + if (!e.notificationFired) + { + var duration = playable.GetDuration(); + var canTrigger = m_PreviousTime <= e.time && e.time <= duration; + if (canTrigger) + { + Trigger_internal(playable, info.output, ref e); + m_Notifications[i] = e; + } + } + } + } + } + + /// <summary> + /// This method is called during the PrepareFrame phase of the PlayableGraph. + /// </summary> + /// <remarks> + /// Called once before processing starts. + /// </remarks> + /// <param name="playable">The reference to the playable associated with this PlayableBehaviour.</param> + /// <param name="info">Playable context information such as weight, evaluationType, and so on.</param> + public override void PrepareFrame(Playable playable, FrameData info) + { + // Never trigger on scrub + if (info.evaluationType == FrameData.EvaluationType.Evaluate) + { + return; + } + + SyncDurationWithExternalSource(playable); + SortNotifications(); + var currentTime = playable.GetTime(); + + // Fire notifications from previousTime till the end + if (info.timeLooped) + { + var duration = playable.GetDuration(); + TriggerNotificationsInRange(m_PreviousTime, duration, info, playable, true); + var dx = playable.GetDuration() - m_PreviousTime; + var nFullTimelines = (int)((info.deltaTime * info.effectiveSpeed - dx) / playable.GetDuration()); + for (var i = 0; i < nFullTimelines; i++) + { + TriggerNotificationsInRange(0, duration, info, playable, false); + } + TriggerNotificationsInRange(0, currentTime, info, playable, false); + } + else + { + var pt = playable.GetTime(); + TriggerNotificationsInRange(m_PreviousTime, pt, info, + playable, true); + } + + for (var i = 0; i < m_Notifications.Count; ++i) + { + var e = m_Notifications[i]; + if (e.notificationFired && CanRestoreNotification(e, info, currentTime, m_PreviousTime)) + { + Restore_internal(ref e); + m_Notifications[i] = e; + } + } + + m_PreviousTime = playable.GetTime(); + } + + void SortNotifications() + { + if (m_NeedSortNotifications) + { + m_Notifications.Sort((x, y) => x.time.CompareTo(y.time)); + m_NeedSortNotifications = false; + } + } + + static bool CanRestoreNotification(NotificationEntry e, FrameData info, double currentTime, double previousTime) + { + if (e.triggerOnce) + return false; + if (info.timeLooped) + return true; + + //case 1111595: restore the notification if the time is manually set before it + return previousTime > currentTime && currentTime <= e.time; + } + + void TriggerNotificationsInRange(double start, double end, FrameData info, Playable playable, bool checkState) + { + if (start <= end) + { + var playMode = Application.isPlaying; + for (var i = 0; i < m_Notifications.Count; i++) + { + var e = m_Notifications[i]; + if (e.notificationFired && (checkState || e.triggerOnce)) + continue; + + var notificationTime = e.time; + if (e.prewarm && notificationTime < end && (e.triggerInEditor || playMode)) + { + Trigger_internal(playable, info.output, ref e); + m_Notifications[i] = e; + } + else + { + if (notificationTime < start || notificationTime > end) + continue; + + if (e.triggerInEditor || playMode) + { + Trigger_internal(playable, info.output, ref e); + m_Notifications[i] = e; + } + } + } + } + } + + void SyncDurationWithExternalSource(Playable playable) + { + if (m_TimeSource.IsValid()) + { + playable.SetDuration(m_TimeSource.GetDuration()); + playable.SetTimeWrapMode(m_TimeSource.GetTimeWrapMode()); + } + } + + static void Trigger_internal(Playable playable, PlayableOutput output, ref NotificationEntry e) + { + output.PushNotification(playable, e.payload); + e.notificationFired = true; + } + + static void Restore_internal(ref NotificationEntry e) + { + e.notificationFired = false; + } + } +} |
