Global Event Manager System

Started by
11 comments, last by Hodgman 11 years, 2 months ago

I know this has been discussed before, but I couldn't find a thread that really answered a few questions that have been bouncing around my head for a few days now. I have in my game a global EventManager system. The way this system works is that any class can override the IEventHandler interface, and register itself to listen for events in the EventManager. Each event name is a string for now, but it will probably be a hashed string later. Then, an event handler must override the HandleEvent method of IEventHandler, and any time an event it is registered for is triggered, this callback is hit.

In theory, this seems like a sound system. However, I have noticed a few anti-patterns I have had to use in order to achieve it. The first is that it is a Singleton. Now, I am well aware of the evils of Singletons, and their effect on code when overused, however I am having a tough time finding another way of doing it. The EventManager is used in nearly every class that does anything meaningful, meaning it would need to be injected as a dependency in a ridiculous number of objects. At this point, I'm asking myself this question: I know Singletons are bad because it creates tight coupling between two classes and it hides the fact that this class needs the Event Manager. But if the EventManager is used by nearly every class, then isn't it inherently tightly coupled with the entire application?

Another anti-pattern that I am noticing is the fact that event passing can be tough to follow and debug. I can't just step through the stack trace to see exactly what is happening when an event is fired. You kind of have to "know" which classes are listening to which events. This works fine now because I'm the only one on this project, but once this project starts becoming bigger, and I bring on new devs, it's going to be a nightmare for them to follow the event handlers (and once the codebase gets large it may be tough for me, the original author, to remember all the places that I'm putting event handlers). So my question is, is there any better way?

Sorry, I know this is kind of a long post, but I hope someone out there that knows more than me can help smile.png. Thanks.

Advertisement

So my question is, is there any better way?

Cheeky answer: Don't use the global event manager anti-pattern tongue.png

Would it be possible to just link individual objects that need to communicate together directly, using delegates?

So my question is, is there any better way?

Cheeky answer: Don't use the global event manager anti-pattern tongue.png

Would it be possible to just link individual objects that need to communicate together directly, using delegates?

I thought of that, but theres two problems I'm seeing with that:

1) It seems to couple classes that inherently have nothing to do with eachother. Example: When a rock drops, I want a sound to play. I don't want my physics engine being coupled with my audio engine. So what's the solution? I could have an intermediary object like "PhysicsRockDropDelegate" that calls the AudioPlayer, but that's not much better...is it?

2) The delegate pattern allows for only one class to be the "delegate". So for instance, when a character moves, nearly every subsystem will need to be notified. How to do this without broadcasting a global event, while also not tightly coupling the character movement system with nearly every other subsystem?

The point of delegates is that the physics and sound systems don't have to know about each other (when contrasting with interfaces, anyway).
The next layer up in the code base, which does know about physics and audio can then glue together those two independent modules.

e.g. In this C# code, when the physics body's HitTheGround method is called, the sound effect's Play method is called, even though physics and sound are completely independent of each other.
class Position { };
//physics module
class RigidBody
{
	public delegate void OnTouch(Position pos);

	public void AddOnTouch(OnTouch e)
	{
		m_onTouchEvents.Add(e);
	}

	public void HitTheGround()
	{
		foreach (var e in m_onTouchEvents)
			e(m_position);
	}

	private List<OnTouch> m_onTouchEvents = new List<OnTouch>();
	private Position m_position = new Position();
}
//audio module
class SoundEffect
{
	public void Play(Position where)
	{
		/*...*/
	}
}
//game (can glue the above together)
class RockEntity
{
	public RockEntity()
	{
		m_body.AddOnTouch(m_sound.Play);
	}
	private RigidBody m_body = new RigidBody();
	private SoundEffect m_sound = new SoundEffect();
}
Which language are you using? I can translate that into C or C++ if you like...

This is a great code sample, thank you. I am using C++, but I don't need a translation, I am more interested in concepts here, and anyway I know C# quite well. I didn't realize you were talking about delegates in the C# sense, I was thinking of them more in the Objective-C sense. From the C# sense, I see what you mean: rather than registering an event with a global "Event Manager", you register the callback method directly with the object that performs the event. I will think on this. Thank you for your suggestions.

I'm building an editor for an engine in C++/win32, and I'm using a simple event system, or rather a delegate system similar to C#. I'm finding it very useful in keeping things modular, and it's sufficient for the needs of a GUI application. Have a looksie: http://code-section.com/entry/2/c-a-simple-event-system

The point of delegates is that the physics and sound systems don't have to know about each other (when contrasting with interfaces, anyway).
The next layer up in the code base, which does know about physics and audio can then glue together those two independent modules.

e.g. In this C# code, when the physics body's HitTheGround method is called, the sound effect's Play method is called, even though physics and sound are completely independent of each other.

[...snip...]

1) Is that your recommended method of managing events, or are you just showing an example of delegates?
2) This pattern is also sometimes referred to as signals-and-slots, right?

3) Do you find it actually beneficial to use in games, or just desktop applications?

Event processing is one of the areas I have difficulty implementing neatly. mellow.png

I am not Hodgman, but I agree with his philosophy on events. These are my views on the subject and do not necessarily represent the views of all Australians.

1) Is that your recommended method of managing events, or are you just showing an example of delegates?

Somewhat both. Events and delegates are 2 different things and each has their place. Delegates don’t replace events, but they offer an alternative and often better way of tickling tackling some things. Also, delegates are basically event-handlers, meaning they are not mutually exclusive, but when you do it this way it is less abstract and much easier to debug.
Abstract global event systems are bad.

2) This pattern is also sometimes referred to as signals-and-slots, right?

Erm, close enough…
Events, delegates, and signals and slots are 3 different things really, but some of that is just semantics and implementation.
Signals are basically events and slots are basically delegates. The main difference is that “signals and slots” refers to the whole send/handle events system whereas delegates are just the things that handle events.

3) Do you find it actually beneficial to use in games, or just desktop applications?

It is certainly more widely useful in desktop applications which are event-driven. Nothing happens until you click a button or type into the keyboard.
Games are different. It is definitely an error to make a global event system that tries to push your game into a heavily event-driven state. Events in games are better suited for handling script-related events, such as a player walking into a certain area of the map and triggering an in-game cinematic.
There are times and places for event-based systems in games, but outside of this example there are very few.

Event processing is one of the areas I have difficulty implementing neatly. mellow.png

Likely a sign that you intuitively realize that event systems in games are not to be used the same way they are in event-based desktop applications.
Event systems are to be used sparingly in games and only after much planning and pondering.
A global event system is an anti-pattern as was already mentioned. Humans are like desktop applications and dogs are like games. Both can eat chocolate cake, but the dog will likely die from it.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Is that your recommended method of managing events, or are you just showing an example of delegates?
Do you find it actually beneficial to use in games, or just desktop applications?

Well I hate global/centralized 'event managers', after having used quite a few very over-complicated ones before, and yes, I much prefer 'plugging components together' like in the example, via delegates/slots/callbacks/functors/what-have-you.
Yes this use useful for games, not just GUIs. Actually, many over-complicated 'entity/component systems' have some kind of complex event routing built into them, when the above example would solve the same problem easier.

That said, these days I do personally like to reduce abstraction in the flow of control in my code, and find that events/callbacks/virtual/etc all obfuscate the flow of control... So, i like to be more explicit about what the 'current operation' is. In the above example, I'd rather have the physics module generate an entire collection of ground-collision-points, and then fire an event, which the sound module uses to then spawn ground-collision audio effects for that entire collection.

It is certainly more widely useful in desktop applications which are event-driven. Nothing happens until you click a button or type into the keyboard.
Games are different. It is definitely an error to make a global event system that tries to push your game into a heavily event-driven state. Events in games are better suited for handling script-related events, such as a player walking into a certain area of the map and triggering an in-game cinematic.
There are times and places for event-based systems in games, but outside of this example there are very few.

Event processing is one of the areas I have difficulty implementing neatly. mellow.png

Likely a sign that you intuitively realize that event systems in games are not to be used the same way they are in event-based desktop applications.
Event systems are to be used sparingly in games and only after much planning and pondering.


I definitely realize the difference - I use the Qt framework, which handles almost everything through signal-and-slots. I use the signals and slots in my non-game applications, but in my games I do two things: I have gamestates with Update(deltaTime) calls for frame-to-frame changes, and I also have events (like OpenMainMenu or CurrentAreaHasChanged) - the vent part is probably the anti-pattern you are talking about, but I can't really see better big-picture solutions despite being not very pleased with it.

Part of my dissatisfaction is the grammatical tense of my events: Some are commands: OpenMainMenu asks/tells the Main Menu to open, while others are messages: CurrentAreaHasChanged lets other systems know that the area has already changed. But they are using the same system, and the whole thing just feels 'off' to me. Right now it's not so bad, since the events are mostly GUI (OpenMainMenu) or input events (MoveForward), but I'm still not completely satisfied with it.

I suppose all my 'command' events should be direct function calls, and only the 'message' events should be events... Hmmm. But having something like "OpenMenuButtonWasPressed" as a message just for another class at a higher level to then directly call a function seems to require more boilerplate code with pretty much the same result.
My system is definitely 'abstract' in that one message is completely unrelated to another. It's 'global' in the sense that any active gamestates receive the messages ("virtual React(message)") and can send messages (thisGameState->GameStructure.SendMessage(message)) but it's not 'global' in the sense of a global variable.

I don't have my thoughts straight on this issue, and definitely appreciate any insight you can give. I've previously read several of the entries on your journal, including this one.

This topic is closed to new replies.

Advertisement