Unity 事件番外篇:事件管理中心(另一种版本)

前置知识:Unity 事件管理中心

本篇博客展示的是事件管理中心的另一种写法。如果大家想了解其中的原理,可以看看前置知识里的介绍。

之前那个版本的事件管理中心,利用 EventHandler 和 EventArgs 匹配任意参数的无返回值方法,具有很强的通用性。但是有一个稍微麻烦的地方:如果要匹配带参数的无返回值方法,需要将参数包装进一个继承自 EventArgs 的自定义类。然后每次触发这种类型的方法时,要 new 一个这种参数包装类;方法内部也要对 EventArgs 进行类型转换,转成匹配的参数包装类。

那么本篇博客提供另一种事件管理中心的写法,用的是对应的泛型来匹配方法的参数。虽然会牺牲一些通用性,但是能够避免上面提到的麻烦之处。

接下来要展示的事件管理中心代码能够匹配最多 4 个参数的无返回值方法,基本适用于日常的开发。如果实在要匹配 4 个参数以上的无返回值方法,可以对代码进行拓展。

上代码:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 事件中心 单例模式对象 (轻量级)
/// </summary>
public class EventCenter : SingletonBase<EventCenter>
{
    
    
    //key —— 事件的名字(比如:怪物死亡,玩家死亡,通关 等等)
    //value —— 对应的是 监听这个事件 对应的委托函数们
    private Dictionary<string, List<Delegate>> eventDic = new Dictionary<string, List<Delegate>>();

    /// <summary>
    /// 监听事件
    /// </summary>
    /// <param name="eventName"></param>
    /// <param name="callback"></param>
    public void AddListenerBase(string eventName, Delegate callback)
    {
    
    
        if (eventDic.ContainsKey(eventName))
        {
    
    
            eventDic[eventName].Add(callback);
        }
        else
        {
    
    
            eventDic.Add(eventName, new List<Delegate>() {
    
     callback});
        }
    }
    /// <summary>
    /// 监听不需要参数传递的事件
    /// </summary>
    /// <param name="eventName"></param>
    /// <param name="callback"></param>
    public void AddListener(string eventName, Action callback)
    {
    
    
        AddListenerBase(eventName, callback);
    }
    /// <summary>
    /// 添加事件监听(1个参数)
    /// </summary>
    /// <param name="name">事件的名字</param>
    /// <param name="action">准备用来处理事件 的委托函数</param>
    public void AddListener<T>(string eventName, Action<T> callback)
    {
    
    
        AddListenerBase(eventName, callback);
    }
    /// <summary>
    /// 添加事件监听(2个参数)
    /// </summary>
    /// <param name="eventName">事件的名字</param>
    /// <param name="callback">准备用来处理事件 的委托函数</param>
    public void AddListener<T1, T2>(string eventName, Action<T1, T2> callback)
    {
    
    
        AddListenerBase(eventName, callback);
    }
    /// <summary>
    /// 添加事件监听(3个参数)
    /// </summary>
    /// <param name="eventName">事件的名字</param>
    /// <param name="callback">准备用来处理事件 的委托函数</param>
    public void AddListener<T1, T2, T3>(string eventName, Action<T1, T2, T3> callback)
    {
    
    
        AddListenerBase(eventName, callback);
    }
    /// <summary>
    /// 添加事件监听(4个参数)
    /// </summary>
    /// <param name="eventName">事件的名字</param>
    /// <param name="callback">准备用来处理事件 的委托函数</param>
    public void AddListener<T1, T2, T3, T4>(string eventName, Action<T1, T2, T3, T4> callback)
    {
    
    
        AddListenerBase(eventName, callback);
    }
    /// <summary>
    /// 移除事件
    /// </summary>
    /// <param name="eventName"></param>
    /// <param name="callback"></param>
    public void RemoveListenerBase(string eventName, Delegate callback)
    {
    
    
        if (eventDic.TryGetValue(eventName, out List<Delegate> eventList))
        {
    
    
            eventList.Remove(callback);
            //事件列表没有事件时,将该事件键值对从字典中移除
            if (eventList.Count == 0)
            {
    
    
                eventDic.Remove(eventName);
            }
        }
    }

    /// <summary>
    /// 移除不需要参数的事件
    /// </summary>
    /// <param name="eventName"></param>
    /// <param name="callback"></param>
    public void RemoveListener(string eventName, Action callback)
    {
    
    
        RemoveListenerBase(eventName, callback);
    }
    /// <summary>
    /// 移除对应的事件监听(1个参数)
    /// </summary>
    /// <param name="eventName">事件的名字</param>
    /// <param name="callback">对应之前添加的委托函数</param>
    public void RemoveListener<T>(string eventName, Action<T> callback)
    {
    
    
        RemoveListenerBase(eventName, callback);
    }
    /// <summary>
    /// 移除对应的事件监听(2个参数)
    /// </summary>
    /// <param name="eventName">事件的名字</param>
    /// <param name="callback">对应之前添加的委托函数</param>
    public void RemoveListener<T1, T2>(string eventName, Action<T1, T2> callback)
    {
    
    
        RemoveListenerBase(eventName, callback);
    }
    /// <summary>
    /// 移除对应的事件监听(3个参数)
    /// </summary>
    /// <param name="eventName">事件的名字</param>
    /// <param name="callback">对应之前添加的委托函数</param>
    public void RemoveListener<T1, T2, T3>(string eventName, Action<T1, T2, T3> callback)
    {
    
    
        RemoveListenerBase(eventName, callback);
    }
    /// <summary>
    /// 移除对应的事件监听(4个参数)
    /// </summary>
    /// <param name="eventName">事件的名字</param>
    /// <param name="callback">对应之前添加的委托函数</param>
    public void RemoveListener<T1, T2, T3, T4>(string eventName, Action<T1, T2, T3, T4> callback)
    {
    
    
        RemoveListenerBase(eventName, callback);
    }
    /// <summary>
    /// 事件触发(不需要参数的)
    /// </summary>
    /// <param name="eventName"></param>
    public void TriggerEvent(string eventName)
    {
    
    
        if (eventDic.ContainsKey(eventName))
        {
    
    
            foreach (Delegate callback in eventDic[eventName])
            {
    
    
                (callback as Action)?.Invoke();
            }
        }
    }
    /// <summary>
    /// 事件触发(1个参数)
    /// </summary>
    /// <param name="eventName">哪一个名字的事件触发了</param>
    public void TriggerEvent<T>(string eventName, T info)
    {
    
    
        if (eventDic.ContainsKey(eventName))
        {
    
                
            foreach (Delegate callback in eventDic[eventName])
            {
    
    
                (callback as Action<T>)?.Invoke(info); 
            }
        }
    }
    /// <summary>
    /// 事件触发(2个参数)
    /// </summary>
    /// <param name="eventName">哪一个名字的事件触发了</param>
    public void TriggerEvent<T1, T2>(string eventName, T1 info1, T2 info2)
    {
    
    
        if (eventDic.ContainsKey(eventName))
        {
    
    
            foreach (Delegate callback in eventDic[eventName])
            {
    
    
                (callback as Action<T1, T2>)?.Invoke(info1, info2);
            }
        }
    }
    /// <summary>
    /// 事件触发(3个参数)
    /// </summary>
    /// <param name="eventName">哪一个名字的事件触发了</param>
    public void TriggerEvent<T1, T2, T3>(string eventName, T1 info1, T2 info2, T3 info3)
    {
    
    
        if (eventDic.ContainsKey(eventName))
        {
    
    
            foreach (Delegate callback in eventDic[eventName])
            {
    
    
                (callback as Action<T1, T2, T3>)?.Invoke(info1, info2, info3);
            }
        }
    }
    /// <summary>
    /// 事件触发(4个参数)
    /// </summary>
    /// <param name="eventName">哪一个名字的事件触发了</param>
    public void TriggerEvent<T1, T2, T3, T4>(string eventName, T1 info1, T2 info2, T3 info3, T4 info4)
    {
    
    
        if (eventDic.ContainsKey(eventName))
        {
    
    
            foreach (Delegate callback in eventDic[eventName])
            {
    
    
                (callback as Action<T1, T2, T3, T4>)?.Invoke(info1, info2, info3, info4);
            }
        }
    }
    /// <summary>
    /// 清空事件中心
    /// </summary>
    public void Clear()
    {
    
    
        eventDic.Clear();
    }
}

事件触发拓展类:

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

/// <summary>
/// 便于触发事件的扩展类
/// </summary>
public static class EventTriggerExt
{
    
    
    /// <summary>
    /// 触发事件(无参数)
    /// </summary>
    /// <param name="sender">触发源</param>
    /// <param name="eventName">事件名</param>
    public static void TriggerEvent(this object sender, string eventName)
    {
    
    
        EventCenter.Instance.TriggerEvent(eventName);
    }
    /// <summary>
    /// 触发事件(1个参数)
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="sender">触发源</param>
    /// <param name="eventName">事件名</param>
    /// <param name="info">参数</param>
    public static void TriggerEvent<T>(this object sender, string eventName, T info)
    {
    
    
        EventCenter.Instance.TriggerEvent(eventName, info);
    }
    /// <summary>
    /// 触发事件(2个参数)
    /// </summary>
    /// <typeparam name="T1"></typeparam>
    /// <typeparam name="T2"></typeparam>
    /// <param name="sender">触发源</param>
    /// <param name="eventName">事件名</param>
    /// <param name="info1">参数1</param>
    /// <param name="info2">参数2</param>
    public static void TriggerEvent<T1, T2>(this object sender, string eventName, T1 info1, T2 info2)
    {
    
    
        EventCenter.Instance.TriggerEvent(eventName, info1, info2);
    }
    /// <summary>
    /// 触发事件(3个参数)
    /// </summary>
    /// <typeparam name="T1"></typeparam>
    /// <typeparam name="T2"></typeparam>
    /// <typeparam name="T3"></typeparam>
    /// <param name="sender">触发源</param>
    /// <param name="eventName">事件名</param>
    /// <param name="info1">参数1</param>
    /// <param name="info2">参数2</param>
    /// <param name="info3">参数3</param>
    public static void TriggerEvent<T1, T2, T3>(this object sender, string eventName, T1 info1, T2 info2, T3 info3)
    {
    
    
        EventCenter.Instance.TriggerEvent(eventName, info1, info2, info3);
    }
    /// <summary>
    /// 触发事件(4个参数)
    /// </summary>
    /// <typeparam name="T1"></typeparam>
    /// <typeparam name="T2"></typeparam>
    /// <typeparam name="T3"></typeparam>
    /// <typeparam name="T4"></typeparam>
    /// <param name="sender">触发源</param>
    /// <param name="eventName">事件名</param>
    /// <param name="info1">参数1</param>
    /// <param name="info2">参数2</param>
    /// <param name="info3">参数3</param>
    /// <param name="info4">参数4</param>
    public static void TriggerEvent<T1, T2, T3, T4>(this object sender, string eventName, T1 info1, T2 info2, T3 info3, T4 info4)
    {
    
    
        EventCenter.Instance.TriggerEvent(eventName, info1, info2, info3, info4);
    }
}

使用方法:

public class Player : MonoBehaviour
{
    
    
    void Update()
    {
    
    
        if (Input.GetKeyDown(KeyCode.J))
        {
    
    
            this.TriggerEvent("PlayerDead", gameObject.name);//运用到了扩展方法
        }
    }  
}
public class GameoverUI : MonoBehaviour
{
    
    
    private void Awake()
    {
    
    
        EventManager.Instance.AddListener<string>(EventName.PlayerDead, ShowGameOver);
    }
    private void OnDestroy()
    {
    
    
        EventManager.Instance.RemoveListener<string>(EventName.PlayerDead, ShowGameOver);
    }
    private void ShowGameOver(string playerName)
    {
    
    
         print($"游戏结束,{playerName}阵亡");     
    }  
}

以上两段代码模拟了“玩家死亡,打开游戏结束 UI”的事件。


此版本的事件管理中心优点:

  • 事件触发和事件监听方法内部的编写更加方便(不需要额外自定义参数包装类)

注意点:

  • 如果要监听有参数的方法,记得添加对应类型,对应数量的泛型
  • 触发一个事件时只有同一种类型的监听方法能被响应。比如一个事件绑定了一个无参无返回值方法和一个带 2 个参数,无返回值方法,在触发事件的时候如果不带参数,那么就只有无参无返回值的方法能被响应。

委托与事件系列:
C#委托(结合 Unity)
C#事件(结合 Unity)
观察者模式(结合C# Unity)
Unity 事件管理中心
事件番外篇:UnityEvent
Unity 事件番外篇:事件管理中心(另一种版本)

猜你喜欢

转载自blog.csdn.net/qq_46044366/article/details/126810076