I developed an event system based on the Publisher/Subscriber pattern.
The publishers will publish an event to a specific channel
The subscribers will subscribe to one or more of the channels and their appropriate method(s) will be invoked when an event is published to that channel.
I’m using an enum for the channels.
I have not tested this out yet in Unity but my hopes are to create a loosely coupled system in terms of event handling. Let me know what you think!
using System;
using System.Collections.Generic;
public enum EventChannel
{
A,
B,
C
}
public class EventObject<T>
{
public readonly object sender;
public readonly T value;
public EventObject(object sender, T value)
{
this.sender = sender;
this.value = value;
}
}
public class EventBus
{
private Dictionary<EventChannel, List<Action<object>>> subscribers = new Dictionary<EventChannel, List<Action<object>>>();
private static EventBus _instance;
public static EventBus Instance
{
get
{
if (_instance == null)
_instance = new EventBus();
return _instance;
}
}
public void Publish<T>(EventChannel channel, object sender, T value)
{
if (!subscribers.ContainsKey(channel))
return;
var eventHandlers = subscribers[channel];
var payload = new EventObject<T>(sender, value);
foreach (var eventHandler in eventHandlers)
{
eventHandler?.Invoke(payload);
}
}
public void Subscribe(EventChannel channel, Action<object> eventHandler)
{
if (subscribers.ContainsKey(channel))
{
List<Action<object>> actionList = subscribers[channel];
if (!actionList.Contains(eventHandler))
{
actionList.Add(eventHandler);
}
}
else
{
List<Action<object>> actionList = new List<Action<object>> { eventHandler };
subscribers.Add(channel, actionList);
}
}
public void Unsubscribe<T>(EventChannel channel, Action<object> eventHandler)
{
if (!subscribers.ContainsKey(channel))
return;
List<Action<object>> actionList = subscribers[channel];
if (actionList.Contains(eventHandler))
{
actionList.Remove(eventHandler);
}
if (actionList.Count == 0)
{
subscribers.Remove(channel);
}
}
}
public interface IPublisher
{
void Publish<T>(EventChannel channel, T value);
}
public class PublisherClass : IPublisher
{
EventBus eventBus;
public PublisherClass()
{
eventBus = EventBus.Instance;
PerformOperation();
Publish(EventChannel.B, "Publisher class finished executing");
}
private void PerformOperation()
{
int score = 10;
Publish(EventChannel.A, score);
}
public void Publish<T>(EventChannel channel, T value)
{
if (eventBus == null)
return;
eventBus.Publish(channel, this, value);
}
}
public interface ISubscriber
{
void Subscribe(EventChannel channel, Action<object> action);
}
public class SubscriberClass : ISubscriber
{
EventBus eventBus;
public SubscriberClass()
{
eventBus = EventBus.Instance;
Subscribe(EventChannel.A, PrintValue);
Subscribe(EventChannel.B, DoSomething);
}
public void Subscribe(EventChannel channel, Action<object> eventHandler)
{
if (eventBus == null)
return;
eventBus.Subscribe(channel, eventHandler);
}
private void PrintValue(object e)
{
EventObject<int> _event = e as EventObject<int>;
if (_event == null)
return;
Console.WriteLine(_event.value);
}
private void DoSomething(object e)
{
Console.WriteLine("Doing something");
}
}
public class Driver
{
static void Main(string[] args)
{
new SubscriberClass();
new PublisherClass();
}
}