using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using UnityEngine.Playables; namespace UnityEngine.Timeline { /// /// Listens for emitted signals and reacts depending on its defined reactions. /// /// A SignalReceiver contains a list of reactions. Each reaction is bound to a SignalAsset. /// When a SignalEmitter emits a signal, the SignalReceiver invokes the corresponding reaction. /// /// [TimelineHelpURL(typeof(SignalReceiver))] public class SignalReceiver : MonoBehaviour, INotificationReceiver { [SerializeField] EventKeyValue m_Events = new EventKeyValue(); /// /// Called when a notification is sent. /// /// The playable that sent the notification. /// The received notification. Only notifications of type will be processed. /// User defined data that depends on the type of notification. Uses this to pass necessary information that can change with each invocation. public void OnNotify(Playable origin, INotification notification, object context) { var signal = notification as SignalEmitter; if (signal != null && signal.asset != null) { UnityEvent evt; if (m_Events.TryGetValue(signal.asset, out evt) && evt != null) { evt.Invoke(); } } } /// /// Defines a new reaction for a SignalAsset. /// /// The SignalAsset for which the reaction is being defined. /// The UnityEvent that describes the reaction. /// Thrown when the asset is null. /// Thrown when the SignalAsset is already registered with this receiver. public void AddReaction(SignalAsset asset, UnityEvent reaction) { if (asset == null) throw new ArgumentNullException("asset"); if (m_Events.signals.Contains(asset)) throw new ArgumentException("SignalAsset already used."); m_Events.Append(asset, reaction); } /// /// Appends a null SignalAsset with a reaction specified by the UnityEvent. /// /// The new reaction to be appended. /// The index of the appended reaction. /// Multiple null assets are valid. public int AddEmptyReaction(UnityEvent reaction) { m_Events.Append(null, reaction); return m_Events.events.Count - 1; } /// /// Removes the first occurrence of a SignalAsset. /// /// The SignalAsset to be removed. public void Remove(SignalAsset asset) { if (!m_Events.signals.Contains(asset)) { throw new ArgumentException("The SignalAsset is not registered with this receiver."); } m_Events.Remove(asset); } /// /// Gets a list of all registered SignalAssets. /// /// Returns a list of SignalAssets. public IEnumerable GetRegisteredSignals() { return m_Events.signals; } /// /// Gets the first UnityEvent associated with a SignalAsset. /// /// A SignalAsset defining the signal. /// Returns the reaction associated with a SignalAsset. Returns null if the signal asset does not exist. public UnityEvent GetReaction(SignalAsset key) { UnityEvent ret; if (m_Events.TryGetValue(key, out ret)) { return ret; } return null; } /// /// Returns the count of registered SignalAssets. /// /// public int Count() { return m_Events.signals.Count; } /// /// Replaces the SignalAsset associated with a reaction at a specific index. /// /// The index of the reaction. /// The replacement SignalAsset. /// Thrown when the replacement SignalAsset is already registered to this SignalReceiver. /// The new SignalAsset can be null. public void ChangeSignalAtIndex(int idx, SignalAsset newKey) { if (idx < 0 || idx > m_Events.signals.Count - 1) throw new IndexOutOfRangeException(); if (m_Events.signals[idx] == newKey) return; var alreadyUsed = m_Events.signals.Contains(newKey); if (newKey == null || m_Events.signals[idx] == null || !alreadyUsed) m_Events.signals[idx] = newKey; if (newKey != null && alreadyUsed) throw new ArgumentException("SignalAsset already used."); } /// /// Removes the SignalAsset and reaction at a specific index. /// /// The index of the SignalAsset to be removed. public void RemoveAtIndex(int idx) { if (idx < 0 || idx > m_Events.signals.Count - 1) throw new IndexOutOfRangeException(); m_Events.Remove(idx); } /// /// Replaces the reaction at a specific index with a new UnityEvent. /// /// The index of the reaction to be replaced. /// The replacement reaction. /// Thrown when the replacement reaction is null. public void ChangeReactionAtIndex(int idx, UnityEvent reaction) { if (idx < 0 || idx > m_Events.events.Count - 1) throw new IndexOutOfRangeException(); m_Events.events[idx] = reaction; } /// /// Gets the reaction at a specific index. /// /// The index of the reaction. /// Returns a reaction. public UnityEvent GetReactionAtIndex(int idx) { if (idx < 0 || idx > m_Events.events.Count - 1) throw new IndexOutOfRangeException(); return m_Events.events[idx]; } /// /// Gets the SignalAsset at a specific index /// /// The index of the SignalAsset. /// Returns a SignalAsset. public SignalAsset GetSignalAssetAtIndex(int idx) { if (idx < 0 || idx > m_Events.signals.Count - 1) throw new IndexOutOfRangeException(); return m_Events.signals[idx]; } // Required by Unity for the MonoBehaviour to have an enabled state private void OnEnable() { } [Serializable] class EventKeyValue { [SerializeField] List m_Signals = new List(); [SerializeField, CustomSignalEventDrawer] List m_Events = new List(); public bool TryGetValue(SignalAsset key, out UnityEvent value) { var index = m_Signals.IndexOf(key); if (index != -1) { value = m_Events[index]; return true; } value = null; return false; } public void Append(SignalAsset key, UnityEvent value) { m_Signals.Add(key); m_Events.Add(value); } public void Remove(int idx) { if (idx != -1) { m_Signals.RemoveAt(idx); m_Events.RemoveAt(idx); } } public void Remove(SignalAsset key) { var idx = m_Signals.IndexOf(key); if (idx != -1) { m_Signals.RemoveAt(idx); m_Events.RemoveAt(idx); } } public List signals { get { return m_Signals; } } public List events { get { return m_Events; } } } } }