Dynamic Event System?

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…

Check this out:
http://unity3d.com/learn/tutorials/modules/intermediate/scripting/delegates

I don’t think delegates alone would do the job. I’m thinking more along the lines of a messenger or notification system.

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
}

Why not just have your scripts subscribe to the events dynamically?

Well take this script for example:

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...
}
  1. I have to hard-code a new event and list every time and there could be tons and tons of them.
  2. 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

Again, wrap each stat in its own object with its own events.

public class Node
{
   public float Amount
   {
     get {return _value;}
     set
     {
       float orignal = _amount;
       if(orignal != value)
         if(OnValueChange!=null)
           OnValueChange.Invoke(orignal, value);
       //Other event checkers
       _amount = value;
     }
   }
   float _amount = 0f;
   public event Action<float,float> OnValueChange;
   public event Action<float,float> OnValueIncrease;
   public event Action<float,float> OnValueDecrease;
}

public class Actor()
{
   Dicionary<int, Node> stats = new Dicionary<int,Node>();
  
   //example only
   public void ChangeNode(int index, float amount)
   {
     stats[index].Amount = amount;
   }
  
   public void ChangeNode(AttirubteEnum index, float amound)
   {
     stats[(int)index].Amount = amount;
   }
  
   void SubscribeToNode(int index, Action<float,float> handler)
   {
     stats[index].OnValueChange+=handler;
   }
  
   void SubscribeToNode(AttributeEnumt index, Action<float,float> handler)
   {
     stats[(int)index].OnValueChange+=handler;
   }
  
   void DefaultSubscribe()
   {
     stats[(int)AttributeEnum.CurrentHealth].OnValueDecrease+=DamageHandler;
   }
  
   void DamageHandler(float orignal, float current)
   {
     //stuff
   }
}

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.

1 Like

@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.

    public delegate void Changed();
    public static event Changed OnChangedX;
    public static event Changed OnChangedY;

yes that is how it works it does fire always to everyone listening

if (OnChanged != null)
    OnChanged();

this condition is just to prevent a crash if no one is listening

i look forward to watching the latest unity live training
http://unity3d.com/learn/live-training/session/events-creating-simple-messaging-system

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.