As I understand it Unity events are much slower than c# delegates and actions. so have followed this modified version which uses delegates action instead of unity events. (second answer down)
And Im trying to pass a reference to the game object, which Triggered the event, to the eventManager and onto all the gameobjects registered to listen to that event. So for example in a custom UI slider script attached to a gameObject, I would write something like this
EventManager.TriggerEvent("volumnSlider", this);
Where (this) refers to the gameObject which contains the custom UI slider script.
So then any gameobject script which is listening for the “volumSlider” event, can access the slider and its properties and methods, for example the percentage of the slider and adjust the games volume accordingly.
This seems to me to be the simplest cleanest and most flexible solution, but I cant find any clear information as to how to do this.
Heres the code Im following All I want to be able to do is substitute the float argument in the trigger event for a reference to the object which fired the event, is this possible how would I change the event manager to achieve this ?
public class EventManager : MonoBehaviour
{
private Dictionary<string, Action<float>> 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 EventManger script on a GameObject in your scene.");
}
else
{
eventManager.Init();
}
}
return eventManager;
}
}
void Init()
{
if (eventDictionary == null)
{
eventDictionary = new Dictionary<string, Action<float>>();
}
}
public static void StartListening(string eventName, Action<float> listener)
{
Action<float> thisEvent;
if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
{
thisEvent += listener;
}
else
{
thisEvent += listener;
instance.eventDictionary.Add(eventName, thisEvent);
}
}
public static void StopListening(string eventName, Action<float> listener)
{
if (eventManager == null) return;
Action<float> thisEvent;
if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
{
thisEvent -= listener;
}
}
public static void TriggerEvent(string eventName, float h)
{
Action<float> thisEvent = null;
if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
{
thisEvent.Invoke(h);
}
}
}
I didn’t read your code, but do some reading on delegates.
If you want to use delegates, you’ll have to first define / declare a delegate type so that it takes whatever parameters you need to pass to your system.
Action is “pre-made” C# delegate and it can be passed no parameters, or some parameters if needed.
If you wanted to just pass a GameObject to methods contained in delegate, it would be something like this.
First define your Action type variable, you can add empty delegate to avoid null checks:
// Action that takes Gameobject as it's only parameter
public Action<GameObject> myGameObjectAction = delegate {};
Then you’ll have to add methods to your delegate/Action type variable, these will be called when delegate/Action is Invoked:
myGameObjectAction += MyGameObjectTakingMethod;
// This could be your method
void MyGameObjectTakingMethod (GameObject gameObject)
{
Debug.Log("GameObject name: " + gameObject.name);
}
Then you can call Invoke on delegate/Action to call the methods that are inside delegate, and here you can pass your GameObject to methods:
Somewhere along the line I got a bit confused between Unity events and C# delegate / action, I read that unity events could only handle primitive types, I didn’t realise it would be as simple as substituting for when using C# delegates, anyway for anyone else who might want to do a similar thing and stumble across this post, heres the full updated code, I did a quick test and all seems to be working fine.
using System.Collections.Generic;
using UnityEngine;
using System;
public class EventManager : MonoBehaviour
{
private Dictionary<string, Action<GameObject>> 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 EventManger script on a GameObject in your scene.");
}
else
{
eventManager.Init();
}
}
return eventManager;
}
}
void Init()
{
if (eventDictionary == null)
{
eventDictionary = new Dictionary<string, Action<GameObject>>();
}
}
public static void StartListening(string eventName, Action<GameObject> listener)
{
Action<GameObject> thisEvent;
if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
{
thisEvent += listener;
}
else
{
thisEvent += listener;
instance.eventDictionary.Add(eventName, thisEvent);
}
}
public static void StopListening(string eventName, Action<GameObject> listener)
{
if (eventManager == null) return;
Action<GameObject> thisEvent;
if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
{
thisEvent -= listener;
}
}
public static void TriggerEvent(string eventName, GameObject go)
{
Action<GameObject> thisEvent = null;
if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
{
thisEvent.Invoke(go);
}
}
}
Then in your script dispatching the even for example a button
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyUIButton : MonoBehaviour
{
public int testVar = 7;
void OnMouseDown()
{
Debug.Log("touch detected ");
EventManager.TriggerEvent("testEvent", this.gameObject);
}
}
First, I can’t believe it’s taken me two days to run across this thread. I’ve been banging my head learning about c# events, delegates, actions, eventHandlers all just to try and get this functionality. With that being said For me the provided code doesn’t completely work as expected. Action is passed by value, after you -= or += the listener you also need to update the dictionary value with the changes so it’s no out-of-date. At least that’s my understanding and some tests on my end seem to verify this. Am I missing something?
public static void StartListening(string eventName, Action<GameObject> listener)
{
Action<GameObject> thisEvent;
if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
{
thisEvent += listener;
//Need to add this back into the Dict
instance.eventDictionary[eventName] = thisEvent;
}
else
{
thisEvent += listener;
instance.eventDictionary.Add(eventName, thisEvent);
}
}
Fortunately I found this post and the solution proposed by AnalogUniverse helped me.
I tried to send a int value inside the event, but reusing the example official code of unity events.
I show my approach for the future because I was hard to setup:
EventManager.cs:
using UnityEngine.Events;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class UnityIntEvent : UnityEvent<int>
{
}
public class EventManager : MonoBehaviour
{
private Dictionary<string, UnityIntEvent> eventIntDictionary;
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();
}
}
return eventManager;
}
}
void Init()
{
if (eventIntDictionary == null)
{
eventIntDictionary = new Dictionary<string, UnityIntEvent>();
}
}
public static void StartIntListening(string eventName, UnityAction<int> listener)
{
UnityIntEvent thisEvent = null;
if (Instance.eventIntDictionary.TryGetValue(eventName, out thisEvent))
{
thisEvent.AddListener(listener);
}
else
{
thisEvent = new UnityIntEvent();
thisEvent.AddListener(listener);
Instance.eventIntDictionary.Add(eventName, thisEvent);
}
}
public static void StopIntListening(string eventName, UnityAction<int> listener)
{
if (eventManager == null) return;
UnityIntEvent thisEvent = null;
if (Instance.eventIntDictionary.TryGetValue(eventName, out thisEvent))
{
thisEvent.RemoveListener(listener);
}
}
public static void TriggerIntEvent(string eventName, int value)
{
UnityIntEvent thisEvent = null;
if (Instance.eventIntDictionary.TryGetValue(eventName, out thisEvent))
{
thisEvent.Invoke(value);
}
}