Event System in C#
For Modern Man Championship, there are a lot of things that happen which trigger behavior. Whether that be audio, a menu effect, or a particle system, there are a lot of disparate systems that all want to know when a specific thing happens. Originally I was using just direct references. However, this got to be messy. I decided I needed an event system.
I could have used the default C# event system, however that required me to still hold on to a reference to whatever fired it, which I wasn't fond of. I did end up using some of the syntax however, just to keep similar interfaces.
My system uses a centralized object to handle subscriptions to events, called an Event Dispatcher. The Event Dispatcher follows a singleton pattern so that subscribers and firers don't need to search for the Dispatcher in the scene. Let's take a look at what it looks like:
Ultimately EventDispatcher is a wrapper for a Dictionary of delegates. The keys are Typed to EventArgs so that users can hook in C# events if they want to. The EventDispatcher lacks any sort of buffering though, so it is entirely possible to send this system into an infinite event loop. Lets take a look at an example event being triggered:
The first thing to note is that this is a singleton, so that the person implementing this (such as 4am me) doesn't accidentally double-create this event system. After that there is the setup for the double buffer of events, so that we don't hit an infinite cycle of events that create sub events back and forth. The way I handle the event listeners is by using a dictionary of GUIDs with delegates inside of each listener's class. I probably could have figured out a better way to do this rather than using GUIDs.
Add listener inserts the listener's delegate into the class. Looking at this method now, I'm noticing that a sloppy coder could accidentally double-add a given delegate, causing nasty things to happen. In addition, I'm not entirely sure why it would be returning a boolean, when the only possibly thing it can return is true. Was it meant to indicate whether or not the delegate was already in there? I suppose it is to keep in line with RemoveListener, which actually does return two different values. Both of these functions are pretty straightforward thankfully.
Enqueueing an event is pretty straightforward too. You get the proper active queue (from 0 to NUM_QUEUES - 1) , you just Enqueue it. Not a whole lot here to mess up, so good job for me not messing that up! One thing that I could have done (which I should have done) is to include an immediate fire functionality, so that an event could circumvent the entire queue system. Starfire was never crazy enough to need something like that, but it would have been nice to have.
Now for the meat of the manager: processing events. In this function I either go through everything in the activeQueue or as much of it as I can before the timer runs out. One problem with this is that I shouldn't have it be a public event; a malicious class could come in an recall this. Looking at it though, it wouldn't necessarily cause a logic issue, but it could lead to a slight stutter in framerate if there are a lot of events and the malicious call is made multiple times a frame. Thankfully both of those conditions have to be true for that to occur though.