//#define EVENTROUTER_THROWEXCEPTIONS #if EVENTROUTER_THROWEXCEPTIONS //#define EVENTROUTER_REQUIRELISTENER // Uncomment this if you want listeners to be required for sending events. #endif using System; using UnityEngine; using UnityEngine.Events; using System.Collections; using System.Collections.Generic; using UnityEngine.Audio; namespace MoreMountains.Tools { /// /// MMGameEvents are used throughout the game for general game events (game started, game ended, life lost, etc.) /// public struct MMGameEvent { static MMGameEvent e; public string EventName; public int IntParameter; public Vector2 Vector2Parameter; public Vector3 Vector3Parameter; public bool BoolParameter; public string StringParameter; public static void Trigger(string eventName, int intParameter = 0, Vector2 vector2Parameter = default(Vector2), Vector3 vector3Parameter = default(Vector3), bool boolParameter = false, string stringParameter = "") { e.EventName = eventName; e.IntParameter = intParameter; e.Vector2Parameter = vector2Parameter; e.Vector3Parameter = vector3Parameter; e.BoolParameter = boolParameter; e.StringParameter = stringParameter; MMEventManager.TriggerEvent(e); } } /// /// This class handles event management, and can be used to broadcast events throughout the game, to tell one class (or many) that something's happened. /// Events are structs, you can define any kind of events you want. This manager comes with MMGameEvents, which are /// basically just made of a string, but you can work with more complex ones if you want. /// /// To trigger a new event, from anywhere, do YOUR_EVENT.Trigger(YOUR_PARAMETERS) /// So MMGameEvent.Trigger("Save"); for example will trigger a Save MMGameEvent /// /// you can also call MMEventManager.TriggerEvent(YOUR_EVENT); /// For example : MMEventManager.TriggerEvent(new MMGameEvent("GameStart")); will broadcast an MMGameEvent named GameStart to all listeners. /// /// To start listening to an event from any class, there are 3 things you must do : /// /// 1 - tell that your class implements the MMEventListener interface for that kind of event. /// For example: public class GUIManager : Singleton, MMEventListener /// You can have more than one of these (one per event type). /// /// 2 - On Enable and Disable, respectively start and stop listening to the event : /// void OnEnable() /// { /// this.MMEventStartListening(); /// } /// void OnDisable() /// { /// this.MMEventStopListening(); /// } /// /// 3 - Implement the MMEventListener interface for that event. For example : /// public void OnMMEvent(MMGameEvent gameEvent) /// { /// if (gameEvent.EventName == "GameOver") /// { /// // DO SOMETHING /// } /// } /// will catch all events of type MMGameEvent emitted from anywhere in the game, and do something if it's named GameOver /// [ExecuteAlways] public static class MMEventManager { private static Dictionary> _subscribersList; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] static void InitializeStatics() { _subscribersList = new Dictionary>(); } static MMEventManager() { _subscribersList = new Dictionary>(); } /// /// Adds a new subscriber to a certain event. /// /// listener. /// The event type. public static void AddListener( MMEventListener listener ) where MMEvent : struct { Type eventType = typeof( MMEvent ); if (!_subscribersList.ContainsKey(eventType)) { _subscribersList[eventType] = new List(); } if (!SubscriptionExists(eventType, listener)) { _subscribersList[eventType].Add( listener ); } } /// /// Removes a subscriber from a certain event. /// /// listener. /// The event type. public static void RemoveListener( MMEventListener listener ) where MMEvent : struct { Type eventType = typeof( MMEvent ); if( !_subscribersList.ContainsKey( eventType ) ) { #if EVENTROUTER_THROWEXCEPTIONS throw new ArgumentException( string.Format( "Removing listener \"{0}\", but the event type \"{1}\" isn't registered.", listener, eventType.ToString() ) ); #else return; #endif } List subscriberList = _subscribersList[eventType]; #if EVENTROUTER_THROWEXCEPTIONS bool listenerFound = false; #endif for (int i = subscriberList.Count-1; i >= 0; i--) { if( subscriberList[i] == listener ) { subscriberList.Remove( subscriberList[i] ); #if EVENTROUTER_THROWEXCEPTIONS listenerFound = true; #endif if ( subscriberList.Count == 0 ) { _subscribersList.Remove(eventType); } return; } } #if EVENTROUTER_THROWEXCEPTIONS if( !listenerFound ) { throw new ArgumentException( string.Format( "Removing listener, but the supplied receiver isn't subscribed to event type \"{0}\".", eventType.ToString() ) ); } #endif } /// /// Triggers an event. All instances that are subscribed to it will receive it (and will potentially act on it). /// /// The event to trigger. /// The 1st type parameter. public static void TriggerEvent( MMEvent newEvent ) where MMEvent : struct { List list; if( !_subscribersList.TryGetValue( typeof( MMEvent ), out list ) ) #if EVENTROUTER_REQUIRELISTENER throw new ArgumentException( string.Format( "Attempting to send event of type \"{0}\", but no listener for this type has been found. Make sure this.Subscribe<{0}>(EventRouter) has been called, or that all listeners to this event haven't been unsubscribed.", typeof( MMEvent ).ToString() ) ); #else return; #endif for (int i=list.Count-1; i >= 0; i--) { ( list[i] as MMEventListener ).OnMMEvent( newEvent ); } } /// /// Checks if there are subscribers for a certain type of events /// /// true, if exists was subscriptioned, false otherwise. /// Type. /// Receiver. private static bool SubscriptionExists( Type type, MMEventListenerBase receiver ) { List receivers; if( !_subscribersList.TryGetValue( type, out receivers ) ) return false; bool exists = false; for (int i = receivers.Count-1; i >= 0; i--) { if( receivers[i] == receiver ) { exists = true; break; } } return exists; } } /// /// Static class that allows any class to start or stop listening to events /// public static class EventRegister { public delegate void Delegate( T eventType ); public static void MMEventStartListening( this MMEventListener caller ) where EventType : struct { MMEventManager.AddListener( caller ); } public static void MMEventStopListening( this MMEventListener caller ) where EventType : struct { MMEventManager.RemoveListener( caller ); } } /// /// Event listener basic interface /// public interface MMEventListenerBase { }; /// /// A public interface you'll need to implement for each type of event you want to listen to. /// public interface MMEventListener : MMEventListenerBase { void OnMMEvent( T eventType ); } public class MMEventListenerWrapper : MMEventListener, IDisposable where TEvent : struct { private Action _callback; private TOwner _owner; public MMEventListenerWrapper(TOwner owner, Action callback) { _owner = owner; _callback = callback; RegisterCallbacks(true); } public void Dispose() { RegisterCallbacks(false); _callback = null; } protected virtual TTarget OnEvent(TEvent eventType) => default; public void OnMMEvent(TEvent eventType) { var item = OnEvent(eventType); _callback?.Invoke(item); } private void RegisterCallbacks(bool b) { if (b) { this.MMEventStartListening(); } else { this.MMEventStopListening(); } } } }