Event system is one of the engine core subsystem since we need to transfer data between different subsystems.
I was thinking about making non-blocking event system with queue feature and flexibility of use.
Brief overview
Let’s see what are the key components of our event system and how they interact with each other.
Firstly, I need to say that we will use some logic of publisher-subscriber pattern, but without correspond classes, just their logic.
So, we have the next components:
Publisher. In our case - any code that send an event.
Manager (bus, pool or any other name that you like). Main job - queue events and dispatch them.
Subscriber/Handler. Just a function that can be a class member function or lambda.
Event. User-defined (Event interface realization) class, that can be processed by manager by uuid.
Let’s try to visualize Event and Manager on the class diagram.
We will not display any “publisher” or “subscriber” classes, because they are only user-dependent.
Next step is to visualize Publisher-Manager-Subscriber interaction on sequence diagram.
Implementation
Event
Let’s start our implementation from the core messaging thing - event.
Our Event interface is pretty simple:
The main point is that every custom Event should have unique identifier, which we will use later in our manager.
For simplicity I will use generated string here, but I would recommend you to hash such strings. I’ve already added hashed strings usage to my engine, so you can check it out here.
Let me to show you example of custom event:
You can make some improvements (if you want), like more convenient uuid definition or debug tostring function (you can find my code here)
Event handler
Fine, now we need to implement event handler. Our handler is just a function (member function or lambda). To hold this type we will use std::function:
Let’s implement event handler wrapper that will be stored in EventManager. I will use Template Method design pattern (template magic 😀) to be able to store any type of event handler with the appropriate event type reference (instead of unsafe pointer to Event):
I think that the code above pretty understandable without in depth explanation. But if you stuck - don’t panic, just leave a comment below and I will try to help you.
Event Manager
The next code block is our event manager class:
I’ve created global variable for our manager, but it has some disadvantages with initialization order and multhireading perspective. So, you are free to make it local variable (maybe in Application class or whatever).
Also I’ve made useful functions to interact with Event Manager from any engine/game subsystem:
As you can see, we use two data structures:
vector for events queue, which contains pointer to events with different types.
unordered_map (hashtable implementation from standard library), where key is event uuid (will be explained in Event section) and value - vector of subscribers-callbacks.
Subscribe/Unsubscribe functions should be called from subscriber’s code.
TriggerEvent/QueueEvent function should be called from pusblisher’s code.
DispatchEvents should be called from the main game loop on every, like this:
Definition of our manager mechanism is next:
Handler example
Let’s see how handler can interact with event manager.
For example, we have camera class, that create projection matrix.
Projection matrix depennds on window size. So, we need to handle window resize event to recalculate projection matrix every time we change window size.
Firstly, we add member function OnWindowResizeEvent, that will be our callback for event.
Our ctor/dtor and callback definition (we initialize std::function handler with lambda):
Publisher example
The last component of our system - publisher.
Actually our publisher is just a function call, TriggerEvent or QueueEvent.
For example, we want to trigger WindowResizeEvent and queue WindowCloseEvent event from window class:
P.S.
I hope you will find something interesting and useful from this post.
I think I will add multithreading support to event system when I will be experimenting with multithreading in the engine and I will write post about it later or expand this post with new changes and improvements.
Feel free to comment and write yor thoughts about my implementation.
Thank you for attention!
References
Game Coding Complete, 4th ed. Mike McShaffry, David Graham.