Maze / Library /PackageCache /com.unity.timeline@1.6.5 /Runtime /Playables /TimeNotificationBehaviour.cs
| 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(); | |
| var currentTime = playable.GetTime(); | |
| for (var i = 0; i < m_Notifications.Count; i++) | |
| { | |
| // case 1257208 - when a timeline is _resumed_, only reset notifications after the resumed time | |
| if (m_Notifications[i].time > currentTime && !m_Notifications[i].triggerOnce) | |
| { | |
| 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; | |
| } | |
| } | |
| } | |