带有委托而不是UnityEvent的Unity EventManager

wei*_*_yu 3 c# unity-game-engine

我正在使用UnityEvent查找Manager的 c#委托版本.我不想使用它,因为UnityEvent在大多数时候比C#事件慢.

有关如何实现这一点的任何线索?

Pro*_*mer 18

你可以使用Action哪个实际上是一个像这样声明的委托:

namespace System
{
    public delegate void Action();
}
Run Code Online (Sandbox Code Playgroud)

1.从使用委托的命名空间中UnityAction替换所有with .ActionSystem

2,全部thisEvent.AddListener(listener);用.替换thisEvent += listener;

3,全部thisEvent.RemoveListener(listener);用.替换 thisEvent -= listener;

以下是移植到使用委托/操作的Unity 原始 版本的修改版本EventManager.

没有参数:

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

public class EventManager : MonoBehaviour
{

    private Dictionary<string, Action> 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>();
        }
    }

    public static void StartListening(string eventName, Action listener)
    {
        Action thisEvent;
        if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
        {
            //Add more event to the existing one
            thisEvent += listener;

            //Update the Dictionary
            instance.eventDictionary[eventName] = thisEvent;
        }
        else
        {
            //Add event to the Dictionary for the first time
            thisEvent += listener;
            instance.eventDictionary.Add(eventName, thisEvent);
        }
    }

    public static void StopListening(string eventName, Action listener)
    {
        if (eventManager == null) return;
        Action thisEvent;
        if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
        {
            //Remove event from the existing one
            thisEvent -= listener;

            //Update the Dictionary
            instance.eventDictionary[eventName] = thisEvent;
        }
    }

    public static void TriggerEvent(string eventName)
    {
        Action thisEvent = null;
        if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
        {
            thisEvent.Invoke();
            // OR USE instance.eventDictionary[eventName]();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

测试脚本:

下面的测试脚本通过每2秒触发一次事件来测试事件.

public class TestScript: MonoBehaviour
{
    private Action someListener;

    void Awake()
    {
        someListener = new Action(SomeFunction);
        StartCoroutine(invokeTest());
    }

    IEnumerator invokeTest()
    {
        WaitForSeconds waitTime = new WaitForSeconds(2);
        while (true)
        {
            yield return waitTime;
            EventManager.TriggerEvent("test");
            yield return waitTime;
            EventManager.TriggerEvent("Spawn");
            yield return waitTime;
            EventManager.TriggerEvent("Destroy");
        }
    }

    void OnEnable()
    {
        EventManager.StartListening("test", someListener);
        EventManager.StartListening("Spawn", SomeOtherFunction);
        EventManager.StartListening("Destroy", SomeThirdFunction);
    }

    void OnDisable()
    {
        EventManager.StopListening("test", someListener);
        EventManager.StopListening("Spawn", SomeOtherFunction);
        EventManager.StopListening("Destroy", SomeThirdFunction);
    }

    void SomeFunction()
    {
        Debug.Log("Some Function was called!");
    }

    void SomeOtherFunction()
    {
        Debug.Log("Some Other Function was called!");
    }

    void SomeThirdFunction()
    {
        Debug.Log("Some Third Function was called!");
    }
}
Run Code Online (Sandbox Code Playgroud)

带参数:

从其他问题来看,大多数人都在问如何支持参数.这里是.您可以使用class/ struct作为参数,然后将要传递的所有变量添加到此类/ struct中的函数中.我将EventParam以此为例.您可以EventParam在此代码末尾添加/删除要在事件结构中传递的变量.

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

public class EventManager : MonoBehaviour
{

    private Dictionary<string, Action<EventParam>> 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<EventParam>>();
        }
    }

    public static void StartListening(string eventName, Action<EventParam> listener)
    {
        Action<EventParam> thisEvent;
        if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
        {
            //Add more event to the existing one
            thisEvent += listener;

            //Update the Dictionary
            instance.eventDictionary[eventName] = thisEvent;
        }
        else
        {
            //Add event to the Dictionary for the first time
            thisEvent += listener;
            instance.eventDictionary.Add(eventName, thisEvent);
        }
    }

    public static void StopListening(string eventName, Action<EventParam> listener)
    {
        if (eventManager == null) return;
        Action<EventParam> thisEvent;
        if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
        {
            //Remove event from the existing one
            thisEvent -= listener;

            //Update the Dictionary
            instance.eventDictionary[eventName] = thisEvent;
        }
    }

    public static void TriggerEvent(string eventName, EventParam eventParam)
    {
        Action<EventParam> thisEvent = null;
        if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
        {
            thisEvent.Invoke(eventParam);
            // OR USE  instance.eventDictionary[eventName](eventParam);
        }
    }
}

//Re-usable structure/ Can be a class to. Add all parameters you need inside it
public struct EventParam
{
    public string param1;
    public int param2;
    public float param3;
    public bool param4;
}
Run Code Online (Sandbox Code Playgroud)

测试脚本:

public class Test : MonoBehaviour
{
    private Action<EventParam> someListener1;
    private Action<EventParam> someListener2;
    private Action<EventParam> someListener3;

    void Awake()
    {
        someListener1 = new Action<EventParam>(SomeFunction);
        someListener2 = new Action<EventParam>(SomeOtherFunction);
        someListener3 = new Action<EventParam>(SomeThirdFunction);

        StartCoroutine(invokeTest());
    }

    IEnumerator invokeTest()
    {
        WaitForSeconds waitTime = new WaitForSeconds(0.5f);

        //Create parameter to pass to the event
        EventParam eventParam = new EventParam();
        eventParam.param1 = "Hello";
        eventParam.param2 = 99;
        eventParam.param3 = 43.4f;
        eventParam.param4 = true;

        while (true)
        {
            yield return waitTime;
            EventManager.TriggerEvent("test", eventParam);
            yield return waitTime;
            EventManager.TriggerEvent("Spawn", eventParam);
            yield return waitTime;
            EventManager.TriggerEvent("Destroy", eventParam);
        }
    }

    void OnEnable()
    {
        //Register With Action variable
        EventManager.StartListening("test", someListener1);
        EventManager.StartListening("Spawn", someListener2);
        EventManager.StartListening("Destroy", someListener3);

        //OR Register Directly to function
        EventManager.StartListening("test", SomeFunction);
        EventManager.StartListening("Spawn", SomeOtherFunction);
        EventManager.StartListening("Destroy", SomeThirdFunction);
    }

    void OnDisable()
    {
        //Un-Register With Action variable
        EventManager.StopListening("test", someListener1);
        EventManager.StopListening("Spawn", someListener2);
        EventManager.StopListening("Destroy", someListener3);

        //OR Un-Register Directly to function
        EventManager.StopListening("test", SomeFunction);
        EventManager.StopListening("Spawn", SomeOtherFunction);
        EventManager.StopListening("Destroy", SomeThirdFunction);
    }

    void SomeFunction(EventParam eventParam)
    {
        Debug.Log("Some Function was called!");
    }

    void SomeOtherFunction(EventParam eventParam)
    {
        Debug.Log("Some Other Function was called!");
    }

    void SomeThirdFunction(EventParam eventParam)
    {
        Debug.Log("Some Third Function was called!");
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 你太棒了! (2认同)

The*_*3n1 5

!! 接受的答案不完整!

作为一个懒惰的程序员,我只是复制了程序员写的内容,但遇到了评论部分的人们遇到的同样的问题。

程序员的解决方案不适用于同一事件的多个订阅者。

这是修复(参数版本的相同更改):

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

public class EventManager : MonoBehaviour
{

    private Dictionary<string, Action> 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>();
        }
    }

    public static void StartListening(string eventName, Action listener)
    {
        if (instance.eventDictionary.ContainsKey(eventName))
        {
            instance.eventDictionary[eventName] += listener;
        }
        else
        {
            instance.eventDictionary.Add(eventName, listener);
        }
    }

    public static void StopListening(string eventName, Action listener)
    {
        if (instance.eventDictionary.ContainsKey(eventName))
        {
            instance.eventDictionary[eventName] -= listener;
        }
    }

    public static void TriggerEvent(string eventName)
    {
        Action thisEvent = null;
        if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
        {
            thisEvent.Invoke();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我在此发布的 StackOverflow 问题的链接

为什么从字典中获取时会得到 Action<> 的克隆?

当您调用 TryGetValue(eventName, out thisEvent) 时,您将提供字典将写入值的引用。您没有获得对字典内部内容的引用(我的意思是,您没有获得指向字典结构的深层指针,这意味着分配给它不会修改字典)。