Created
January 24, 2024 00:56
-
-
Save LambdaSix/bfbce4b5f24573be073db95e9eee6c0a to your computer and use it in GitHub Desktop.
Unity EventManager
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Concurrent; | |
using System.Collections.Generic; | |
using UnityEngine; | |
public abstract class GameEvent | |
{ | |
} | |
public class EventManager : MonoBehaviour, IDisposable | |
{ | |
public bool LimitQueueProcessing { get; set; } = false; | |
public float QueueProcessTime { get; set; } = 0.0f; | |
private static EventManager _instance = null; | |
private ConcurrentQueue<GameEvent> _eventQueue = new(); | |
public delegate void EventDelegate<T>(T e) where T : GameEvent; | |
private delegate void EventDelegate (GameEvent e); | |
private ConcurrentDictionary<Type, EventDelegate> delegates = new(); | |
private ConcurrentDictionary<Delegate, EventDelegate> delegateLookup = new(); | |
private ConcurrentDictionary<Delegate, Delegate> onceLookups = new(); | |
public static EventManager Instance => | |
_instance ??= GameObject.FindObjectOfType(typeof(EventManager)) as EventManager; | |
private EventDelegate Add<T>(EventDelegate<T> @delegate) where T : GameEvent | |
{ | |
if (delegateLookup.ContainsKey(@delegate)) | |
return null; | |
EventDelegate internalDelegate = (e) => @delegate((T)e); | |
delegateLookup[@delegate] = internalDelegate; | |
if (delegates.TryGetValue(typeof(T), out var tempDel)) | |
{ | |
delegates[typeof(T)] = tempDel += internalDelegate; | |
} | |
else | |
{ | |
delegates[typeof(T)] = internalDelegate; | |
} | |
return internalDelegate; | |
} | |
public void Subscribe<T>(EventDelegate<T> callback) where T : GameEvent | |
{ | |
Add(callback); | |
} | |
public void SubscribeOnce<T>(EventDelegate<T> callback) where T : GameEvent | |
{ | |
var result = Add(callback); | |
if (result != null) | |
onceLookups[result] = callback; | |
} | |
public void Unsubscribe<T>(EventDelegate<T> callback) where T : GameEvent | |
{ | |
if (delegateLookup.TryGetValue(callback, out var internalDelegate)) | |
{ | |
if (delegates.TryGetValue(typeof(T), out var tempDel)) | |
{ | |
tempDel -= internalDelegate; | |
if (tempDel is null) | |
{ | |
delegates.Remove(typeof(T), out _); | |
} | |
else | |
{ | |
delegates[typeof(T)] = tempDel; | |
} | |
} | |
delegateLookup.Remove(callback, out _); | |
} | |
} | |
public void UnsubscribeAll() | |
{ | |
delegates.Clear(); | |
delegateLookup.Clear(); | |
onceLookups.Clear(); | |
} | |
public bool HasListener<T>(EventDelegate<T> callback) where T : GameEvent | |
=> delegateLookup.ContainsKey(callback); | |
public void Trigger(GameEvent e) | |
{ | |
var eType = e.GetType(); | |
if (delegates.TryGetValue(eType, out var callback)) | |
{ | |
callback?.Invoke(e); | |
foreach (EventDelegate k in delegates[eType].GetInvocationList()) | |
{ | |
delegates[eType] -= k; | |
if (delegates[eType] is null) | |
{ | |
delegates.Remove(eType, out _); | |
} | |
delegateLookup.Remove(onceLookups[k], out _); | |
onceLookups.Remove(k, out _); | |
} | |
} | |
else | |
{ | |
Debug.LogWarning($"EventManager: Event : {eType} has no listeners."); | |
} | |
} | |
public bool Enqueue(GameEvent evt) | |
{ | |
if (!delegates.ContainsKey(evt.GetType())) | |
{ | |
Debug.LogWarning($"EventManager: QueueEvent() failed due to no listeners for event: {evt.GetType()}"); | |
return false; | |
} | |
_eventQueue.Enqueue(evt); | |
return true; | |
} | |
// Once per update cycle process the queue. Optionally limit processing time | |
void Update() | |
{ | |
float elapsedTime = 0.0f; | |
while (_eventQueue.Count > 0) | |
{ | |
if (LimitQueueProcessing && elapsedTime > QueueProcessTime) | |
return; | |
if (_eventQueue.TryDequeue(out var evt)) | |
{ | |
Trigger(evt); | |
if (LimitQueueProcessing) | |
elapsedTime += Time.deltaTime; | |
} | |
} | |
} | |
public void OnApplicationQuit() | |
{ | |
Dispose(); | |
} | |
public void Dispose() | |
{ | |
this.UnsubscribeAll(); | |
_eventQueue.Clear(); | |
_instance = null; | |
GC.SuppressFinalize(this); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment