diff --git a/Assets/Fungus/Scripts/Components/Clickable2D.cs b/Assets/Fungus/Scripts/Components/Clickable2D.cs index 93c56112..e36d2434 100644 --- a/Assets/Fungus/Scripts/Components/Clickable2D.cs +++ b/Assets/Fungus/Scripts/Components/Clickable2D.cs @@ -39,13 +39,7 @@ namespace Fungus return; } - // TODO: Cache these objects for faster lookup - var handlers = GameObject.FindObjectsOfType(); - for (int i = 0; i < handlers.Length; i++) - { - var handler = handlers[i]; - handler.OnObjectClicked(this); - } + EventDispatcher.Raise(new ObjectClicked.ObjectClickedEvent(this)); } protected virtual void DoPointerEnter() diff --git a/Assets/Fungus/Scripts/Components/Draggable2D.cs b/Assets/Fungus/Scripts/Components/Draggable2D.cs index 68c661ec..ad6f227e 100644 --- a/Assets/Fungus/Scripts/Components/Draggable2D.cs +++ b/Assets/Fungus/Scripts/Components/Draggable2D.cs @@ -4,6 +4,7 @@ using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Serialization; +using System.Collections.Generic; namespace Fungus { @@ -40,6 +41,23 @@ namespace Fungus protected Vector3 newPosition; protected Vector3 delta = Vector3.zero; + #region DragCompleted handlers + protected List dragCompletedHandlers = new List(); + + public void RegisterHandler(DragCompleted handler) + { + dragCompletedHandlers.Add(handler); + } + + public void UnregisterHandler(DragCompleted handler) + { + if(dragCompletedHandlers.Contains(handler)) + { + dragCompletedHandlers.Remove(handler); + } + } + #endregion + protected virtual void LateUpdate() { // iTween will sometimes override the object position even if it should only be affecting the scale, rotation, etc. @@ -57,20 +75,7 @@ namespace Fungus { return; } - - var dragEnteredHandlers = GetHandlers(); - for (int i = 0; i < dragEnteredHandlers.Length; i++) - { - var handler = dragEnteredHandlers[i]; - handler.OnDragEntered(this, other); - } - - var dragCompletedHandlers = GetHandlers(); - for (int i = 0; i < dragCompletedHandlers.Length; i++) - { - var handler = dragCompletedHandlers[i]; - handler.OnDragEntered(this, other); - } + EventDispatcher.Raise(new DragEntered.DragEnteredEvent(this, other)); } protected virtual void OnTriggerExit2D(Collider2D other) @@ -79,26 +84,7 @@ namespace Fungus { return; } - - var dragExitedHandlers = GetHandlers(); - for (int i = 0; i < dragExitedHandlers.Length; i++) - { - var handler = dragExitedHandlers[i]; - handler.OnDragExited(this, other); - } - - var dragCompletedHandlers = GetHandlers(); - for (int i = 0; i < dragCompletedHandlers.Length; i++) - { - var handler = dragCompletedHandlers[i]; - handler.OnDragExited(this, other); - } - } - - protected virtual T[] GetHandlers() where T : EventHandler - { - // TODO: Cache these object for faster lookup - return GameObject.FindObjectsOfType(); + EventDispatcher.Raise(new DragExited.DragExitedEvent(this, other)); } protected virtual void DoBeginDrag() @@ -111,12 +97,7 @@ namespace Fungus startingPosition = transform.position; - var dragStartedHandlers = GetHandlers(); - for (int i = 0; i < dragStartedHandlers.Length; i++) - { - var handler = dragStartedHandlers[i]; - handler.OnDragStarted(this); - } + EventDispatcher.Raise(new DragStarted.DragStartedEvent(this)); } protected virtual void DoDrag() @@ -144,38 +125,32 @@ namespace Fungus bool dragCompleted = false; - var handlers = GetHandlers(); - for (int i = 0; i < handlers.Length; i++) + for (int i = 0; i < dragCompletedHandlers.Count; i++) { - var handler = handlers[i]; - if (handler.DraggableObject == this) + var handler = dragCompletedHandlers[i]; + if (handler != null && handler.DraggableObject == this) { if (handler.IsOverTarget()) { - handler.OnDragCompleted(this); dragCompleted = true; - if (returnOnCompleted) - { - LeanTween.move(gameObject, startingPosition, returnDuration).setEase(LeanTweenType.easeOutExpo); - } + EventDispatcher.Raise(new DragCompleted.DragCompletedEvent(this)); } } } if (!dragCompleted) { - var dragCancelledHandlers = GetHandlers(); - for (int i = 0; i < dragCancelledHandlers.Length; i++) - { - var handler = dragCancelledHandlers[i]; - handler.OnDragCancelled(this); - } + EventDispatcher.Raise(new DragCancelled.DragCancelledEvent(this)); if (returnOnCancelled) { LeanTween.move(gameObject, startingPosition, returnDuration).setEase(LeanTweenType.easeOutExpo); } } + else if(returnOnCompleted) + { + LeanTween.move(gameObject, startingPosition, returnDuration).setEase(LeanTweenType.easeOutExpo); + } } protected virtual void DoPointerEnter() diff --git a/Assets/Fungus/Scripts/Components/EventHandler.cs b/Assets/Fungus/Scripts/Components/EventHandler.cs index 602c8eb7..39881ae5 100644 --- a/Assets/Fungus/Scripts/Components/EventHandler.cs +++ b/Assets/Fungus/Scripts/Components/EventHandler.cs @@ -41,6 +41,38 @@ namespace Fungus [FormerlySerializedAs("parentSequence")] [SerializeField] protected Block parentBlock; + #region Unity Messages Propagation + protected virtual void UnityOnEnable() + { + + } + + protected virtual void UnityOnDisable() + { + + } + + protected virtual void UnityStart() + { + + } + + void OnEnable() + { + UnityOnEnable(); + } + + void OnDisable() + { + UnityOnDisable(); + } + + void Start() + { + UnityStart(); + } + #endregion + #region Public members /// diff --git a/Assets/Fungus/Scripts/EventHandlers/DragCancelled.cs b/Assets/Fungus/Scripts/EventHandlers/DragCancelled.cs index 5a369111..4bf4b01a 100644 --- a/Assets/Fungus/Scripts/EventHandlers/DragCancelled.cs +++ b/Assets/Fungus/Scripts/EventHandlers/DragCancelled.cs @@ -14,9 +14,34 @@ namespace Fungus [AddComponentMenu("")] public class DragCancelled : EventHandler { + public class DragCancelledEvent + { + public Draggable2D DraggableObject; + public DragCancelledEvent(Draggable2D draggableObject) + { + DraggableObject = draggableObject; + } + } + [Tooltip("Draggable object to listen for drag events on")] [SerializeField] protected Draggable2D draggableObject; + protected override void UnityOnEnable() + { + base.UnityOnEnable(); + EventDispatcher.AddListener(OnDragCancelledEvent); + } + + protected override void UnityOnDisable() + { + base.UnityOnDisable(); + EventDispatcher.RemoveListener(OnDragCancelledEvent); + } + + void OnDragCancelledEvent(DragCancelledEvent evt) + { + OnDragCancelled(evt.DraggableObject); + } #region Public members public virtual void OnDragCancelled(Draggable2D draggableObject) diff --git a/Assets/Fungus/Scripts/EventHandlers/DragCompleted.cs b/Assets/Fungus/Scripts/EventHandlers/DragCompleted.cs index 63240d76..140b71af 100644 --- a/Assets/Fungus/Scripts/EventHandlers/DragCompleted.cs +++ b/Assets/Fungus/Scripts/EventHandlers/DragCompleted.cs @@ -14,6 +14,14 @@ namespace Fungus [AddComponentMenu("")] public class DragCompleted : EventHandler { + public class DragCompletedEvent + { + public Draggable2D DraggableObject; + public DragCompletedEvent(Draggable2D draggableObject) + { + DraggableObject = draggableObject; + } + } [Tooltip("Draggable object to listen for drag events on")] [SerializeField] protected Draggable2D draggableObject; @@ -24,6 +32,47 @@ namespace Fungus // we have to listen to the callbacks and track the touching state ourselves. protected bool overTarget = false; + protected override void UnityOnEnable() + { + base.UnityOnEnable(); + EventDispatcher.AddListener(OnDragCompletedEvent); + EventDispatcher.AddListener(OnDragEnteredEvent); + EventDispatcher.AddListener(OnDragExitedEvent); + + if(draggableObject != null) + { + draggableObject.RegisterHandler(this); + } + } + + protected override void UnityOnDisable() + { + base.UnityOnDisable(); + EventDispatcher.RemoveListener(OnDragCompletedEvent); + EventDispatcher.RemoveListener(OnDragEnteredEvent); + EventDispatcher.RemoveListener(OnDragExitedEvent); + + if(draggableObject != null) + { + draggableObject.UnregisterHandler(this); + } + } + + void OnDragCompletedEvent(DragCompletedEvent evt) + { + OnDragCompleted(evt.DraggableObject); + } + + void OnDragEnteredEvent(DragEntered.DragEnteredEvent evt) + { + OnDragEntered(evt.DraggableObject, evt.TargetCollider); + } + + void OnDragExitedEvent(DragExited.DragExitedEvent evt) + { + OnDragExited(evt.DraggableObject, evt.TargetCollider); + } + #region Public members /// diff --git a/Assets/Fungus/Scripts/EventHandlers/DragEntered.cs b/Assets/Fungus/Scripts/EventHandlers/DragEntered.cs index 3b9674f0..c55cf46c 100644 --- a/Assets/Fungus/Scripts/EventHandlers/DragEntered.cs +++ b/Assets/Fungus/Scripts/EventHandlers/DragEntered.cs @@ -17,12 +17,38 @@ namespace Fungus [AddComponentMenu("")] public class DragEntered : EventHandler { + public class DragEnteredEvent + { + public Draggable2D DraggableObject; + public Collider2D TargetCollider; + public DragEnteredEvent(Draggable2D draggableObject, Collider2D targetCollider) + { + DraggableObject = draggableObject; + TargetCollider = targetCollider; + } + } [Tooltip("Draggable object to listen for drag events on")] [SerializeField] protected Draggable2D draggableObject; [Tooltip("Drag target object to listen for drag events on")] [SerializeField] protected Collider2D targetObject; + protected override void UnityOnEnable() + { + base.UnityOnEnable(); + EventDispatcher.AddListener(OnDragEnteredEvent); + } + + protected override void UnityOnDisable() + { + base.UnityOnDisable(); + EventDispatcher.RemoveListener(OnDragEnteredEvent); + } + + void OnDragEnteredEvent(DragEnteredEvent evt) + { + OnDragEntered(evt.DraggableObject, evt.TargetCollider); + } #region Public members /// diff --git a/Assets/Fungus/Scripts/EventHandlers/DragExited.cs b/Assets/Fungus/Scripts/EventHandlers/DragExited.cs index 9381a7d6..711a9505 100644 --- a/Assets/Fungus/Scripts/EventHandlers/DragExited.cs +++ b/Assets/Fungus/Scripts/EventHandlers/DragExited.cs @@ -17,12 +17,40 @@ namespace Fungus [AddComponentMenu("")] public class DragExited : EventHandler { + public class DragExitedEvent + { + public Draggable2D DraggableObject; + public Collider2D TargetCollider; + public DragExitedEvent(Draggable2D draggableObject, Collider2D targetCollider) + { + DraggableObject = draggableObject; + TargetCollider = targetCollider; + } + } + [Tooltip("Draggable object to listen for drag events on")] [SerializeField] protected Draggable2D draggableObject; [Tooltip("Drag target object to listen for drag events on")] [SerializeField] protected Collider2D targetObject; + protected override void UnityOnEnable() + { + base.UnityOnEnable(); + EventDispatcher.AddListener(OnDragEnteredEvent); + } + + protected override void UnityOnDisable() + { + base.UnityOnDisable(); + EventDispatcher.RemoveListener(OnDragEnteredEvent); + } + + void OnDragEnteredEvent(DragExitedEvent evt) + { + OnDragExited(evt.DraggableObject, evt.TargetCollider); + } + #region Public members /// diff --git a/Assets/Fungus/Scripts/EventHandlers/DragStarted.cs b/Assets/Fungus/Scripts/EventHandlers/DragStarted.cs index 90d00a81..95f14570 100644 --- a/Assets/Fungus/Scripts/EventHandlers/DragStarted.cs +++ b/Assets/Fungus/Scripts/EventHandlers/DragStarted.cs @@ -14,8 +14,34 @@ namespace Fungus [AddComponentMenu("")] public class DragStarted : EventHandler { + public class DragStartedEvent + { + public Draggable2D DraggableObject; + public DragStartedEvent(Draggable2D draggableObject) + { + DraggableObject = draggableObject; + } + } + [SerializeField] protected Draggable2D draggableObject; + protected override void UnityOnEnable() + { + base.UnityOnEnable(); + EventDispatcher.AddListener(OnDragStartedEvent); + } + + protected override void UnityOnDisable() + { + base.UnityOnDisable(); + EventDispatcher.RemoveListener(OnDragStartedEvent); + } + + void OnDragStartedEvent(DragStartedEvent evt) + { + OnDragStarted(evt.DraggableObject); + } + #region Public members /// diff --git a/Assets/Fungus/Scripts/EventHandlers/EventDispatcher.cs b/Assets/Fungus/Scripts/EventHandlers/EventDispatcher.cs new file mode 100644 index 00000000..4f999570 --- /dev/null +++ b/Assets/Fungus/Scripts/EventHandlers/EventDispatcher.cs @@ -0,0 +1,174 @@ +using System.Collections.Generic; +using System; + +namespace Fungus +{ + public class EventDispatcher + { + public delegate void TypedDelegate(T e) where T : class; + + #region Statics + static EventDispatcher _instance; + public static EventDispatcher Instance + { + get + { + if(_instance == null) + { + _instance = new EventDispatcher(); + } + return _instance; + } + } + + public static void AddLog(Action log) + { + Instance.addLog(log); + } + + public static void RemoveLog(Action log) + { + Instance.removeLog(log); + } + + public static void AddListener(TypedDelegate listener) where T : class + { + Instance.addListener(listener); + } + + public static void RemoveListener(TypedDelegate listener) where T : class + { + Instance.removeListener(listener); + } + + public static void Raise(T evt) where T : class + { + Instance.raise(evt); + } + + public static void Raise() where T : class, new() + { + Instance.raise(new T()); + } + + public static void UnregisterAll() + { + Instance.unregisterAll(); + } + #endregion + + #region Private Members + readonly Dictionary> _delegates; + event Action _onLog; + #endregion + + #region Private Functions + private EventDispatcher() + { + _delegates = new Dictionary>(); + } + /// + /// Gets the delegate list copy. + /// + /// + /// As listener can modify the list while iterating it, it is better to iterate a copy of the delegates list instead of a reference. + /// + /// A copy of the delegates list if found. Null if the dictionary does not contain a delegate list for this event. + /// Event instance. + /// Type of the received event. + List getDelegateListCopy(T evt) + { + var type = typeof(T); + return _delegates.ContainsKey(type) ? new List(_delegates[type]) : null; + } + + void log(string message) + { + if(_onLog != null) + { + _onLog(message); + } + } + #endregion + + #region Public Functions + public void addLog(Action log) + { + _onLog += log; + } + + public void removeLog(Action log) + { + _onLog -= log; + } + + public void addListener(TypedDelegate listener) where T : class + { + var type = typeof(T); + if(!_delegates.ContainsKey(type)) + { + _delegates.Add(type, new List()); + } + + var list = _delegates[type]; + if(!list.Contains(listener)) + { + list.Add(listener); + } + } + + public void removeListener(TypedDelegate listener) where T : class + { + var type = typeof(T); + if(_delegates.ContainsKey(type)) + { + _delegates[type].Remove(listener); + return; + } + } + + public void raise(T evt) where T : class + { + if(evt == null) + { + log("Raised a null event"); + return; + } + + var list = getDelegateListCopy(evt); + if(list == null || list.Count < 1) + { + log("Raised an event with no listeners"); + return; + } + + for(int i = 0; i < list.Count; ++i) + { + var callback = list[i] as TypedDelegate; + + if(callback != null) + { + try + { + callback(evt); + } + catch(Exception gotcha) + { + log(gotcha.Message); + } + } + } + } + + public void raise() where T : class, new() + { + raise(new T()); + } + + public void unregisterAll() + { + _delegates.Clear(); + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/Fungus/Scripts/EventHandlers/EventDispatcher.cs.meta b/Assets/Fungus/Scripts/EventHandlers/EventDispatcher.cs.meta new file mode 100644 index 00000000..031a08d7 --- /dev/null +++ b/Assets/Fungus/Scripts/EventHandlers/EventDispatcher.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: d6bacc8888852c94bab5c3b36cdf1574 +timeCreated: 1483696929 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Fungus/Scripts/EventHandlers/FlowchartEnabled.cs b/Assets/Fungus/Scripts/EventHandlers/FlowchartEnabled.cs index 18c10305..72c95c05 100644 --- a/Assets/Fungus/Scripts/EventHandlers/FlowchartEnabled.cs +++ b/Assets/Fungus/Scripts/EventHandlers/FlowchartEnabled.cs @@ -14,8 +14,9 @@ namespace Fungus [AddComponentMenu("")] public class FlowchartEnabled : EventHandler { - protected virtual void OnEnable() + protected override void UnityOnEnable() { + base.UnityOnEnable(); // Blocks use coroutines to schedule command execution, but Unity's coroutines are // sometimes unreliable when enabling / disabling objects. // To workaround this we execute the block on the next frame. diff --git a/Assets/Fungus/Scripts/EventHandlers/GameStarted.cs b/Assets/Fungus/Scripts/EventHandlers/GameStarted.cs index 70d4667e..dd793ec9 100644 --- a/Assets/Fungus/Scripts/EventHandlers/GameStarted.cs +++ b/Assets/Fungus/Scripts/EventHandlers/GameStarted.cs @@ -18,7 +18,13 @@ namespace Fungus [Tooltip("Wait for a number of frames after startup before executing the Block. Can help fix startup order issues.")] [SerializeField] protected int waitForFrames = 1; - protected virtual IEnumerator Start() + protected override void UnityStart() + { + base.UnityStart(); + StartCoroutine(GameStartCoroutine()); + } + + protected virtual IEnumerator GameStartCoroutine() { int frameCount = waitForFrames; while (frameCount > 0) diff --git a/Assets/Fungus/Scripts/EventHandlers/ObjectClicked.cs b/Assets/Fungus/Scripts/EventHandlers/ObjectClicked.cs index a103a1ef..e326e6b0 100644 --- a/Assets/Fungus/Scripts/EventHandlers/ObjectClicked.cs +++ b/Assets/Fungus/Scripts/EventHandlers/ObjectClicked.cs @@ -15,12 +15,39 @@ namespace Fungus [AddComponentMenu("")] public class ObjectClicked : EventHandler { + public class ObjectClickedEvent + { + public Clickable2D ClickableObject; + public ObjectClickedEvent(Clickable2D clickableObject) + { + ClickableObject = clickableObject; + } + } + [Tooltip("Object that the user can click or tap on")] [SerializeField] protected Clickable2D clickableObject; [Tooltip("Wait for a number of frames before executing the block.")] [SerializeField] protected int waitFrames = 1; + + protected override void UnityOnEnable() + { + base.UnityOnEnable(); + EventDispatcher.AddListener(OnObjectClickedEvent); + } + + protected override void UnityOnDisable() + { + base.UnityOnDisable(); + EventDispatcher.RemoveListener(OnObjectClickedEvent); + } + + void OnObjectClickedEvent(ObjectClickedEvent evt) + { + OnObjectClicked(evt.ClickableObject); + } + /// /// Executing a block on the same frame that the object is clicked can cause /// input problems (e.g. auto completing Say Dialog text). A single frame delay