Using an EventManager and events to change parameters into another script

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:

using System;
using System.Collections.Generic;
using UnityEngine;

public class EventManager : MonoBehaviour
{
    private Dictionary<string, Action<Dictionary<string, object>>> eventDictionary;

    private static EventManager eventManager;

    public static EventManager instance
    {
        get
        {
            if (!eventManager)
            {
                eventManager = FindObjectOfType(typeof(EventManager)) as EventManager;

                if (!eventManager)
                {
                    Debug.LogError("There needs to be one active EventManager script on a GameObject in your scene.");
                }
                else
                {
                    eventManager.Init();

                    //  Sets this to not be destroyed when reloading scene
                    DontDestroyOnLoad(eventManager);
                }
            }
            return eventManager;
        }
    }

    void Init()
    {
        if (eventDictionary == null)
        {
            eventDictionary = new Dictionary<string, Action<Dictionary<string, object>>>();
        }
    }

    public static void StartListening(string eventName, Action<Dictionary<string, object>> listener)
    {
        Action<Dictionary<string, object>> thisEvent;

        if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
        {
            thisEvent += listener;
            instance.eventDictionary[eventName] = thisEvent;
        }
        else
        {
            thisEvent += listener;
            instance.eventDictionary.Add(eventName, thisEvent);
        }
    }

    public static void StopListening(string eventName, Action<Dictionary<string, object>> listener)
    {
        if (eventManager == null) return;
        Action<Dictionary<string, object>> thisEvent;
        if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
        {
            thisEvent -= listener;
            instance.eventDictionary[eventName] = thisEvent;
        }
    }

    public static void TriggerEvent(string eventName, Dictionary<string, object> message)
    {
        Action<Dictionary<string, object>> thisEvent = null;
        if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
        {
            thisEvent.Invoke(message);
        }
    }
}

The event generator is another object in the scene. The event is triggered when a certain variable reaches a certain value:

        if (annualInib >= 0.95 * theIntegrator.IMax)
        {
            EventManager.TriggerEvent("stopStagione", new Dictionary<string, object> { { "kI", 0.3 } });
        }

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):

    void OnEnable()
    {
        EventManager.StartListening("stopStagione", StopStagione);
    }

    void OnDisable()
    {
        EventManager.StopListening("stopStagione", StopStagione);
    }

    void StopStagione(Dictionary<string, object> message)
    {
        var amount = (double)message["kI"];
        kI = amount;
    }

public override void RatesOfChange(double[] x, double[] xdot, double t)
    {
        setup = GameObject.FindGameObjectWithTag("Setup").GetComponent<SetupNew>();
        treeInfo = setup.StaticGeneticInfo;

        xdot[0] = alpha * x[0] * (1 - (x[0] / x[1])); // lunghezza dL/dt
        xdot[1] = 1 - (setup.Manager.TotalLenght() / maxTreeHeight) - 0.2 * (x[2] / iMax); // Lmax
        xdot[2] = c * (alpha * x[0] * (1-(x[0]/x[1]))) - kI * x[2]; // Inibitore dI/dt
   
    }

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!

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.

1 Like

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

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.

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

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.

Sorry, probably I was not so much clear.

        if (annualInib >= 0.95 * theIntegrator.IMax)
        {
            EventManager.TriggerEvent("stopStagione", new Dictionary<string, object> { { "kI", 0.3 } });
        }

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().

bool didIt;
if (!didIt && (annualInib >= 0.95 * theIntegrator.IMax))
        {
            didIt = true;
            EventManager.TriggerEvent("stopStagione", new Dictionary<string, object> { { "kI", 0.3 } });
        }
1 Like