Hello!
After reading up on ways to make the code in my project more maintainable (less complex, less coupling, etc), I’ve been looking into making my game more relying on events for handling things.
The way i see, I can get rid of many direct dependencies and tight coupling between classes by using an eventsystem instead of direct function calls between them, this would make the classes themselves more isolated and easier to keep track of, is there any downsides of this kind of approach?
Also, currently im using a static eventmanager (c# delegate events), is this the way to go about it?
TLDR; Is using a Static EventManager to reduce coupling and complexity a good idea?
EDIT: Another question that came to mind. In Visual Studios there the tool “Code Metrics” which gives some pretty decent information on Maintainability and the things related to it, Is it an accurate tool to use for Unity projects as well?
Also to note is that the “Cyclomatic Complexity” for my EventManager is through the roof compared to other classes, but maybe this is just the result of many objects utilizing events?
I have previously worked on projects where I used some central event manager like class with custom event arguments.
It is nice way of doing things because you can really decouple things and have less dependent architecture but keep in mind that you have to decouple events from delegates upon object destruction because Unity will not do that for you and you can get some really weird bugs if you don’t do that.
I also suggest that you look in to Actions they are sort of ad hoc delegates that are very handy.
If you are looking in to way to make your project more maintainable i suggest you check SOLID design principles that can really help your architecture.
Well the idea of the EventManager i’d be using is to simply have a Static EventManager class which keeps track of all events that can be raised, if any object in the scene want to subscibe to that event they just add themselves the list of functions to be executed, since the EventManager is Static, everybody can reach it. As for fireing the events, that is done in a similar way.
I guess the benefit of this design is that for the objects interested in an event, it’s irrelevant who actually invoked the event, and vice versa.
Did you experience that the EventManager grew really big due to being a lot of events to keep track of?
Also, i’ve looked in the SOLID design principle previously and i try to adhere to that best i can, but sometimes it’s hard to predict how the interactions within the program will be and I always miss something that comes up later, which leads to ducttape solutions which in turn leads to a nightmare when it comes to maintainance.
Another question that came to mind. In Visual Studios there the tool “Code Metrics” which gives some pretty decent information on Maintainability and the things related to it, Is it an accurate tool to use for Unity projects as well?
Also to note is that the “Cyclomatic Complexity” for my EventManager is through the roof compared to other classes, but maybe this is just the result of many objects utilizing events?
Yes in some cases event class really grew and while that was not really that huge issue because it is basically just a list of different events sometimes it is hard to keep track of all the events that can be fired and you loose track does event already exists especially if you have multiple programmers working on project!
While it works i find it not really elegant. At the moment I am experimenting instead of using central hub for all events use of already mentioned Actions so basically events are then separated and they are located in specific class that does “that certain thing” I found that this way basically I track more easily what is happening because logic is more like if I want to know my enemy died I will tell enemy class to tell me not I will call event manager and find OnEnemyDied(EnemyDiedEventArgs arg) event and pass my callback for it. So then I can also form some interfaces for general type of events like IPlayerEventReceiver where I can pass this interface to for example Player class that can handle invocation. Also there can be IStandardGamePawnEmiter and class that implements would emit this standard events and I can then each game character that needs this can implement this interface to send out this events. This in turn helps also when more people are working because you don’t step on each other toes and it’s harder to implement some event twice. Still playing with the logic trying to find something more elegant with this approach.
This in turn helps me use solid a little bit better because then everything is more decoupled and can be easily replaced or updated.
I am not sure about Visual Studio code metrics haven’t used it but for sure so many objects referring to events makes for quite complex architecture since you have EventManager hub where a lot of objects are sort of converging.
Cool, I was first thinking that you wanted to replicate all of the internal events, but what you are suggesting sounds really cool. I think on the back in you would have to program the event receiver on each object. If I am following you correctly, or are you anticipating?
So let me put forth a theory of what you are proposing:
Say you have a group of people, each of them subscribes to the manager.
One person gets attacked by a monster, he sends out an event that says “I am being attacked”
The event manager sends the event off to each person, who in turn answers his own questions like “Am I close enough?”, “Do I care?” “If I care, do I do something about it?”, “What do I do?”
You would also, I guess, have to unsubscribe if the person died, or left the area where the player is.
Sorry, just looking for a practical solution for this type of manager.
It’s not quite the way you put forward it, im simly using the built-in events in C#.
If i’ll use the scenario you’ve put forward.
Say you have a group of people, each of them subscribes a function to a specific event they are intereted in (reference held by EventManager)
One person gets attacked by a monster, he tells the EventManager to trigger the event of being attacked.
The EventManager triggers the event, which is basically just a list of functions (delegates), which then executes all the functions “subscribed” to that event.
As I stated previously, this seems to me like a good idea, since the person triggering the event of being attacked doesn’t need to know who is “listening” to that event, and persons listening to the event doesn’t need to know where or who it’s triggered by.
The downside i can see with this approach is you’re in a team of developers, unless naming of the events are very clear on what the event represents, there could be some confusion of when/if to trigger an event in a specific situation and for the listeners to figure out if subscribing to an event has the desired effect.