I want to create a dynamic event system. I want to create events such as “OnStatChanged”, “OnDamageTaken”, “OnAbilityCasted”, etc.
Those are just examples because there could be tons of events. I’d also like to have specific versions of those events; “On[AbilityName]Casted”, “On[StatName]Changed”, etc.
I want to be able to dynamically add and remove those events from GameObjects and then be able to add actions for those events.
So far I was thinking of creating an Event class that would take a string name property and a dictionary with the key being the name of the event. I’m not too fond of using strings though. I know I could use constants but still…
I’m assuming that the events will be based upon numerical changes?
If so, wrap each stat and their events in an object.
class Actor
{
Dictionary<int, Node> Nodes; // or whatever you want as key
//I usually use an integer and convert from enums
}
class Node : BaseNode<int>
{
//node specific code
}
class BaseNode<ValueType>
{
protected ValueType value;
//events here
}
public class Example : MonoBehaviour{
List<Ability> onAbilityCasted;
List<Ability> onAbilityDamageReceived;
List<Ability> onAbilityDamageDealt;
//And so on
void OnAbilityCasted(){
//for each ability in appropriate list, activate
}
void OnFireballCasted(){
//for each ability in appropriate list, activate
}
void OnDamageReceived(){
//for each ability in appropriate list, activate
}
void OnDamageDealt(){
//for each ability in appropriate list, activate
}
void OnDamageDealtOver100(){
//for each ability in appropriate list, activate
}
void OnStatDecreased(){
//for each ability in appropriate list, activate
}
//And so on...
}
I have to hard-code a new event and list every time and there could be tons and tons of them.
Even if I did the above, most objects wouldn’t implement even half of them, which is a huge waste and would pollute the inspector.
What I want to do is something like this:
//In a random monobehaviour
public void ChangeStat(Stat stat, float newValue){
//change the stat
gameObject.GetComponent<Events>().RaiseEvent("StatChanged + stat.Name");
}
//In the event class
Dictionary<string, Delegate> eventDicionary; //Delegate dict???
public void RaiseEvent(string name){
eventDictionary[name].Invoke(); // or whatever. but this could be a list of actions or abilities.
}
The problem I have with the above sample code I posted is: 1) strings, 2) parameters. I want a system that is reliable and can transmit some information about the event
Forget Delegates and C# events for this. As you have noticed this gets very messy as the code base gets bigger. Ultimately it relies on a lot of hard wiring in the inspector, or in the script. Your classes all get closely coupled again. Nothing is maintainable or easy to read or debug. And changing things can be very difficult.
For a system like this you are better off implementing a messaging system. Have a central manager classes that receives events from every class that can produce them. The central manager can also be subscribed too by classes that whish to receive messages. Typically you allow messages to contain a type, so classes can describe what type of messages they wish to receive.
This central messaging system is a common design pattern. I can’t remember its formal name, but a quick google search should find it for you.
@Random_Civilian
This isn’t just about stats though. It’s about all kinds of events, like spell casted, spell activated, on buff added, on death, on spawn, on level up, etc.
Has anyone ever used the WarCraft III editor? I’ve been thinking about implementing a similar system. Basically you have “Triggers” which work like this: Event (something happens) => Condition (if conditions are met) => Actions (do stuff)
That way I could write very generic events or messages or whatever I choose which would pass on some data. The condition part I’d have to work on making some generic conditions which might be the hard part (haven’t thought too much about this yet) and then a list of actions to perform. Triggers with similar events & conditions could be merged if wanted.
I think mixing this with a messenging system could prove very powerful.
In your sample-- what if I want to have OnChanged: height only AND OnChanged: width only? Also it should be reversed–the event should fire regardless of any condition so everyone who listens to it can receive it. Then those listening to it will check the data of the event to see if they should act.
You’re hard-coding extra events all the time you need them. What if you want 100 different events? I am not going to hard-code 100 different events.
So far I’ve looked deeper into Advanced C# Messenger which gave me some ideas, because I want each event listener to check some conditions after the event has been fired and if those conditions are true, execute a set of actions.