Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Using an EventManager and events to change parameters into another script

Discussion in 'Scripting' started by capocchione, Jul 30, 2020.

  1. capocchione

    capocchione

    Joined:
    Dec 9, 2019
    Posts:
    47
    Hello,
    I'm trying to code some kind of EventManager to use events to change parameters into another script.
    The EventManager.cs, attached to an empty object "EventManager" in the scene is coded as follow:


    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class EventManager : MonoBehaviour
    6. {
    7.     private Dictionary<string, Action<Dictionary<string, object>>> eventDictionary;
    8.  
    9.     private static EventManager eventManager;
    10.  
    11.     public static EventManager instance
    12.     {
    13.         get
    14.         {
    15.             if (!eventManager)
    16.             {
    17.                 eventManager = FindObjectOfType(typeof(EventManager)) as EventManager;
    18.  
    19.                 if (!eventManager)
    20.                 {
    21.                     Debug.LogError("There needs to be one active EventManager script on a GameObject in your scene.");
    22.                 }
    23.                 else
    24.                 {
    25.                     eventManager.Init();
    26.  
    27.                     //  Sets this to not be destroyed when reloading scene
    28.                     DontDestroyOnLoad(eventManager);
    29.                 }
    30.             }
    31.             return eventManager;
    32.         }
    33.     }
    34.  
    35.     void Init()
    36.     {
    37.         if (eventDictionary == null)
    38.         {
    39.             eventDictionary = new Dictionary<string, Action<Dictionary<string, object>>>();
    40.         }
    41.     }
    42.  
    43.     public static void StartListening(string eventName, Action<Dictionary<string, object>> listener)
    44.     {
    45.         Action<Dictionary<string, object>> thisEvent;
    46.  
    47.         if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
    48.         {
    49.             thisEvent += listener;
    50.             instance.eventDictionary[eventName] = thisEvent;
    51.         }
    52.         else
    53.         {
    54.             thisEvent += listener;
    55.             instance.eventDictionary.Add(eventName, thisEvent);
    56.         }
    57.     }
    58.  
    59.     public static void StopListening(string eventName, Action<Dictionary<string, object>> listener)
    60.     {
    61.         if (eventManager == null) return;
    62.         Action<Dictionary<string, object>> thisEvent;
    63.         if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
    64.         {
    65.             thisEvent -= listener;
    66.             instance.eventDictionary[eventName] = thisEvent;
    67.         }
    68.     }
    69.  
    70.     public static void TriggerEvent(string eventName, Dictionary<string, object> message)
    71.     {
    72.         Action<Dictionary<string, object>> thisEvent = null;
    73.         if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
    74.         {
    75.             thisEvent.Invoke(message);
    76.         }
    77.     }
    78. }
    The event generator is another object in the scene. The event is triggered when a certain variable reaches a certain value:


    Code (CSharp):
    1.         if (annualInib >= 0.95 * theIntegrator.IMax)
    2.         {
    3.             EventManager.TriggerEvent("stopStagione", new Dictionary<string, object> { { "kI", 0.3 } });
    4.         }
    As the code above, when the annualInib is >= 0.95 * theIntegrator.IMax, the event is triggered and the parameter I want to change
    kI
    is passed with the new value I want to set
    kI = 0.3


    The listener is in another script where I need to change the parameter passed by the event and do some calculation (not complete code below):


    Code (CSharp):
    1.     void OnEnable()
    2.     {
    3.         EventManager.StartListening("stopStagione", StopStagione);
    4.     }
    5.  
    6.     void OnDisable()
    7.     {
    8.         EventManager.StopListening("stopStagione", StopStagione);
    9.     }
    10.  
    11.     void StopStagione(Dictionary<string, object> message)
    12.     {
    13.         var amount = (double)message["kI"];
    14.         kI = amount;
    15.     }
    16.  
    17. public override void RatesOfChange(double[] x, double[] xdot, double t)
    18.     {
    19.         setup = GameObject.FindGameObjectWithTag("Setup").GetComponent<SetupNew>();
    20.         treeInfo = setup.StaticGeneticInfo;
    21.  
    22.         xdot[0] = alpha * x[0] * (1 - (x[0] / x[1])); // lunghezza dL/dt
    23.         xdot[1] = 1 - (setup.Manager.TotalLenght() / maxTreeHeight) - 0.2 * (x[2] / iMax); // Lmax
    24.         xdot[2] = c * (alpha * x[0] * (1-(x[0]/x[1]))) - kI * x[2]; // Inibitore dI/dt
    25.    
    26.     }
    Now, the event is triggered when annualInib is >= 0.95 * theIntegrator.IMax (I've checked this with a code break in debug mode) but the kI value in the last code block does not change. Also, the event is constantly triggered because the if-statement is always true when annualInib is >= 0.95 * theIntegrator.IMax.

    So, I would like to trigger the event ONCE when annualInib reaches the value of 0.95 * theIntegrator.IMax and change the kI value until another event happens and changes back the value of kI.

    How can I do this?
    Thank you!
     
    Last edited: Jul 30, 2020
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,517
    You're really the only one who can debug this... you need to answer questions such as, if you put a breakpoint on line 14 above (where it assigns
    kI
    ) does it get hit? Does the value make sense? Is any of the code running, etc.
     
    capocchione likes this.
  3. capocchione

    capocchione

    Joined:
    Dec 9, 2019
    Posts:
    47
    Thanks for the answer. No, the breakpoint where you said does not get hit. That's my problem: It looks like the event is triggered (second code block) but the listener (last code block) is not getting it. I am asking for help also to check if I wrote everything right
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,517
    I've only just used the standard C# events... I see you've rolled your own mechanism that does some sort of business decisions in there based on separate bookkeeping. It appears fine but you're going to have to debug the actual steps of adding a listener. After the listener is added, is there an appropriate dictionary entry? Is it named right? When the event is invoked does it find the entry, etc.
     
  5. capocchione

    capocchione

    Joined:
    Dec 9, 2019
    Posts:
    47
    I'm going to chek all of this.
    Also, what about my second issue?? Is there a way to trigger an event ONCE? Because with the if-statement I've the event always triggered because the if parameter is always true
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,517
    Sure, whoever is in charge of triggering the event ones marks a boolean that says "I triggered this event" and then doesn't do it again until conditions are detected that reset the boolean. Nothing unusual about that. It's just up to how you design who is in charge of that decision.
     
  7. capocchione

    capocchione

    Joined:
    Dec 9, 2019
    Posts:
    47
    Sorry, probably I was not so much clear.


    Code (CSharp):
    1.         if (annualInib >= 0.95 * theIntegrator.IMax)
    2.         {
    3.             EventManager.TriggerEvent("stopStagione", new Dictionary<string, object> { { "kI", 0.3 } });
    4.         }
    The if-statement above, triggers the event when
    annualInib >= 0.95 * theIntegrator.IMax
    . When this happens, this stays true all the time so the event is always triggered each Update(). I need a way to trigger the event once when
    annualInib >= 0.95 * theIntegrator.IMax
    even if the if-statement stays true at each Update().
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,517
    Code (csharp):
    1. bool didIt;

    Code (csharp):
    1. if (!didIt && (annualInib >= 0.95 * theIntegrator.IMax))
    2.         {
    3.             didIt = true;
    4.             EventManager.TriggerEvent("stopStagione", new Dictionary<string, object> { { "kI", 0.3 } });
    5.         }
     
    capocchione likes this.