Looking for feedback on Event System I developed

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! :slight_smile:

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();
	}
}
2 Likes

Excellent job! Kudos from me :slight_smile:

1 Like

It’s a good idea. I came across another method using ScriptableObjects which would be worth looking at. It’s in the video I believe is called overthrowing the tyrany of monobehaviour with scriptableobjects. You can find it on youtube.

1 Like

Everything looks good, though the proof is in the pudding, actually getting it into Unity. Like MichaelP, my favorite model for this is using a ScriptableObject as the publisher.

1 Like

Thank you! :slight_smile:

Oh thanks for the suggestion, ill look into it!

Thank you, and yes true haha. I’ve been able to integrate this into the current game I’ve made and so far everything is working really good (except for some minor adjustments I had to make).

But yes I agree, I don’t have much experience using ScriptableObjects in this way but they do seem like the way to go for this!

I haven’t seen events done like this using generics and conditions for subscribing and unsubscribing even after watching many different tutorials on it. I would be very interested in a tutorial or breakdown on how this code works and why it’s designed this way.

1 Like

I appreciate the feedback! Yeah I’d be interested to create a video on this, I’ll keep you posted if I do.

Pretty much I was trying to build an event system in C# that reflects a similar workflow to the Event Listeners in JavaScript, e.g. document.getElementById("div").addEventListener("click", clickHandler);

1 Like

Yeah, I’m still interested, let me know if you do.

1 Like

I have an Article written about this.

I also have a complete Library that use it.

All of the above are:

One thing to note is that Sets and Elements could be a good replacement in projects that use Enums for things that can change, i.e. The Stats, the Character classes.

3 Likes

Sounds good! Life is quite busy right now but hopefully within the coming months I’ll make a video on it.

I actually read your article on this, after building my custom event system. Your blog has been a huge help! I mostly use ScriptableObjects now when I’m working with events in Unity.

1 Like

Amazing! I was just about to ask a question about re-architecting my events (I’m coming from the Turn Based Strategy Course) and this thread popped up in the suggestions. I’ve been getting a little bit lost in all the events so I thought up an architecture very similar to this. My background is in web based services so this architecture came intuitively to me based on prior experience.

I only had the vaguest idea of Scriptable Objects so I love having a reference here due to all the replies. I’ve only skimmed the code, but Scriptable Objects REALLY seem to simplify events.

1 Like

Privacy & Terms