前置知识: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 事件番外篇:事件管理中心(另一种版本)

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

  1. Python番外篇:Python代码生成春联 三种版本

    Hello,大家好,我是wangzirui32,今天就是虎年春节了,先祝大家虎虎生威,虎年大吉!愿大家在新的一年里万事如意,心想事成! 文章目录 1. 普通版本 1.1 引入所需模块 1.2 生成春联 ...

  2. MongoDB 教程番外篇之管理工具: Rockmongo

    RockMongo是PHP5写的一个MongoDB管理工具. 通过 Rockmongo 你可以管理 MongoDB服务,数据库,集合,文档,索引等等. 它提供了非常人性化的操作.类似 phpMyAdm ...

  3. 白话空间统计番外篇:中位数中心算法

    昨天在介绍中位数中心算法的时候,挖了个巨大的坑,结果导致老夫一夜没有睡好,脑子里面飞来飞去全部都是各种选择和迭代算法,今天终于下定决心把这个坑给填上. 其实我一直是不愿意填算法坑的--主要是自己的数学 ...

  4. MongoDB 教程番外篇之添加用户及设置用户权限 ( Rockmongo登陆设置 )

    继上一篇 MongoDB 教程番外篇之管理工具: Rockmongo ,MongoDB 缺省是没有设置鉴权的,业界大部分使用 MongoDB 的项目也没有设置访问权限.这就意味着只要知道 MongoD ...

  5. OpenCV-Python实战(番外篇)——OpenCV中利用鼠标事件动态绘制图形

    OpenCV-Python实战(番外篇)--OpenCV中利用鼠标事件动态绘制图形 使用鼠标事件动态绘制 动态绘制图形 动态绘制图形和文本 相关链接 使用鼠标事件动态绘制 我们已经在<OpenC ...

  6. 【Unity】Avatar与AvatarMask系统介绍(TPS.番外篇)

    [Unity]Avatar与AvatarMask系统介绍(TPS.番外篇) 一些动画知识 动画文件格式 Avatar系统-Unity统一骨骼的解决方案 AvatarMask-部分动画的应用 这次也是拖 ...

  7. 给深度学习入门者的Python快速教程 - 番外篇之Python-OpenCV

    转载自:https://zhuanlan.zhihu.com/p/24425116 本篇是前面两篇教程:给深度学习入门者的Python快速教程 - 基础篇 给深度学习入门者的Python快速教程 - ...

  8. [zt]数学之美番外篇:平凡而又神奇的贝叶斯方法

    数学之美番外篇:平凡而又神奇的贝叶斯方法 Tags: 数学, 机器学习与人工智能, 计算机科学 save it69 saved tags: 贝叶斯 math bayesian algorithm 数学 ...

  9. 转:数学之美番外篇:平凡而又神奇的贝叶斯方法 收藏

    为什么80%的码农都做不了架构师?>>>    转自:http://blog.csdn.net/pongba/archive/2008/09/21/2958094.aspx 数学之美 ...

最新文章

  1. css中元素居中总结
  2. 3.1.4 操作系统之内存的分配与回收
  3. Python词云学习之旅
  4. 陈百强原来这么帅_外套假两件,原来这么帅。
  5. percona-toolkit 之 【pt-slave-delay】说明
  6. 如何查看linux系统版本信息及CPU信息
  7. 如何让 Timer 在特定时间点触发?
  8. ado jet 反复连接 未指定的错误_为什么驱动器的PE端要和变压器中性点有可靠连接?...
  9. 10年腾讯技术专家有话对你说
  10. iOS POST 上传图片
  11. 虚拟机安装panabit详细图解
  12. php 如何查看vc版本信息,PHP版本中的VC6,VC9,VC11,TS,NTS区别
  13. SRS4.0源码分析-序言
  14. SpringBoot集成SwaggerUI及其使用
  15. iOS开发者比较好用的几个工具
  16. 什么是ETF量化接口?
  17. 洗牌算法java 剑指_扑克牌的完美洗牌算法
  18. Android笔记--自定义控件仿遥控器的圆形上下左右OK圆盘按钮
  19. [创业之路-71] :创业思维与打工思维的区别
  20. PTA团队天梯赛║L1-059 敲笨种

热门文章

  1. 教师计算机提升工程培训心得,中小学教师信息技术能力提升工程培训心得3篇...
  2. 09 游戏画面管理与切换控制
  3. Linux的使用入门
  4. 微信小程序 云开发 提交表单数据和表单里上传的多张图片一起提交到数据库里完整代码
  5. 2020-08-06Downie_4中文 PJ
  6. Oracle基于固化视图的表选择性同步
  7. 转使用mIRC下载软件电子书籍
  8. 雷莫芦单抗|cas947687-13-0分子式:C285H434N74O88S2
  9. 西子奥的斯电梯服务器使用教程方法_OTIS电梯服务器简单使用说明
  10. 心电记录电路设计(框图/波形以及信号放大器的选择)