Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Dynamic Event System?

Discussion in 'Scripting' started by Deleted User, Apr 19, 2015.

  1. Deleted User

    Deleted User

    Guest

    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...
     
  2. Juice-Tin

    Juice-Tin

    Joined:
    Jul 22, 2012
    Posts:
    233
  3. Deleted User

    Deleted User

    Guest

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

    Random_Civilian

    Joined:
    Nov 5, 2014
    Posts:
    55
    I'm assuming that the events will be based upon numerical changes?
    If so, wrap each stat and their events in an object.

    Code (CSharp):
    1. class Actor
    2. {
    3. Dictionary<int, Node> Nodes;     // or whatever you want as key
    4. //I usually use an integer and convert from enums
    5. }
    6.  
    7. class Node : BaseNode<int>
    8. {
    9. //node specific code
    10. }
    11.  
    12. class BaseNode<ValueType>
    13. {
    14. protected ValueType value;
    15. //events here
    16. }
     
  5. frankrs

    frankrs

    Joined:
    Aug 29, 2009
    Posts:
    300
    Why not just have your scripts subscribe to the events dynamically?
     
  6. Deleted User

    Deleted User

    Guest

    Well take this script for example:

    Code (CSharp):
    1. public class Example : MonoBehaviour{
    2.  
    3. List<Ability> onAbilityCasted;
    4. List<Ability> onAbilityDamageReceived;
    5. List<Ability> onAbilityDamageDealt;
    6. //And so on
    7.  
    8. void OnAbilityCasted(){
    9. //for each ability in appropriate list, activate
    10. }
    11. void OnFireballCasted(){
    12. //for each ability in appropriate list, activate
    13. }
    14. void OnDamageReceived(){
    15. //for each ability in appropriate list, activate
    16. }
    17. void OnDamageDealt(){
    18. //for each ability in appropriate list, activate
    19. }
    20. void OnDamageDealtOver100(){
    21. //for each ability in appropriate list, activate
    22. }
    23. void OnStatDecreased(){
    24. //for each ability in appropriate list, activate
    25. }
    26. //And so on...
    27. }
    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:

    Code (CSharp):
    1. //In a random monobehaviour
    2.  
    3. public void ChangeStat(Stat stat, float newValue){
    4. //change the stat
    5. gameObject.GetComponent<Events>().RaiseEvent("StatChanged + stat.Name");
    6. }
    7.  
    8. //In the event class
    9. Dictionary<string, Delegate> eventDicionary; //Delegate dict???
    10. public void RaiseEvent(string name){
    11. eventDictionary[name].Invoke(); // or whatever. but this could be a list of actions or abilities.
    12. }
    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
     
  7. Random_Civilian

    Random_Civilian

    Joined:
    Nov 5, 2014
    Posts:
    55
    Again, wrap each stat in its own object with its own events.
    Code (CSharp):
    1.  
    2. public class Node
    3. {
    4.    public float Amount
    5.    {
    6.      get {return _value;}
    7.      set
    8.      {
    9.        float orignal = _amount;
    10.        if(orignal != value)
    11.          if(OnValueChange!=null)
    12.            OnValueChange.Invoke(orignal, value);
    13.        //Other event checkers
    14.        _amount = value;
    15.      }
    16.    }
    17.    float _amount = 0f;
    18.    public event Action<float,float> OnValueChange;
    19.    public event Action<float,float> OnValueIncrease;
    20.    public event Action<float,float> OnValueDecrease;
    21. }
    22.  
    23. public class Actor()
    24. {
    25.    Dicionary<int, Node> stats = new Dicionary<int,Node>();
    26.  
    27.    //example only
    28.    public void ChangeNode(int index, float amount)
    29.    {
    30.      stats[index].Amount = amount;
    31.    }
    32.  
    33.    public void ChangeNode(AttirubteEnum index, float amound)
    34.    {
    35.      stats[(int)index].Amount = amount;
    36.    }
    37.  
    38.    void SubscribeToNode(int index, Action<float,float> handler)
    39.    {
    40.      stats[index].OnValueChange+=handler;
    41.    }
    42.  
    43.    void SubscribeToNode(AttributeEnumt index, Action<float,float> handler)
    44.    {
    45.      stats[(int)index].OnValueChange+=handler;
    46.    }
    47.  
    48.    void DefaultSubscribe()
    49.    {
    50.      stats[(int)AttributeEnum.CurrentHealth].OnValueDecrease+=DamageHandler;
    51.    }
    52.  
    53.    void DamageHandler(float orignal, float current)
    54.    {
    55.      //stuff
    56.    }
    57. }
    58.  
     
  8. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    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.
     
    Deleted User likes this.
  9. Deleted User

    Deleted User

    Guest

    @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.
     
  10. Deleted User

    Deleted User

    Guest

    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.
     
  11. rakkarage

    rakkarage

    Joined:
    Feb 3, 2014
    Posts:
    683
    Code (CSharp):
    1.     public delegate void Changed();
    2.     public static event Changed OnChangedX;
    3.     public static event Changed OnChangedY;
    yes that is how it works it does fire always to everyone listening
    Code (CSharp):
    1. if (OnChanged != null)
    2.     OnChanged();
    this condition is just to prevent a crash if no one is listening
     
  12. rakkarage

    rakkarage

    Joined:
    Feb 3, 2014
    Posts:
    683
  13. Deleted User

    Deleted User

    Guest

    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.