zoukankan      html  css  js  c++  java
  • Unity3D中利用Action实现自己的消息管理(订阅/发布)类

    引言

    一般的软件开发过程中,为了方便对项目进行管理、维护和扩展,通常会采用一种MVC框架,以将显示逻辑、业务逻辑和数据进行分离。

    这在传统企业软件的开发中很常见,但我在使用Unity做游戏开发的时候却几乎找不到相关框架。

    其原因猜测大概有两点,一是游戏开发模式多变,不同类型的游戏代码结构差异很大,很难有一个适用性很强的框架出现;二是Unity太年轻,其大范围使用也不过是最近三四年的事情。

    没有框架也不是意味着没有办法,MVC只是一种规范,只要在开发过程中对代码的组织结构及用途做一定的约束,就能达到各层分离的效果。

    在代码分层组织的结构中,出于解耦合的需求,通常需要一个对事件/消息进行管理的类,以便在各层之间传送消息,其功能包括事件/消息的订阅、发布以及取消订阅。

    本文不会写怎么实现一个MVC结构(驾驭不了),只说说这个事件管理类的实现方法。

    事件管理类的实现

    1、编写EventManager类

    一个事件管理类通过包括三个功能,订阅、发布消息、取消订阅,对应到代码中,也是就三个方法:AddEvent、DispatchEvent、RemoveEvent,还有一个字典List,对订阅事件做管理,实现如下:

      1 /**
      2 * UnityVersion: 2018.3.1f1
      3 * FileName:     EventManager.cs
      4 * Author:       TYQ
      5 * CreateTime:   2019/04/04 15:49:53
      6 * Description:  自定义的事件派发类
      7 */
      8 using System;
      9 using System.Collections;
     10 using System.Collections.Generic;
     11 using UnityEngine;
     12 
     13 public class EventManager
     14 {
     15     /// <summary>
     16     /// 带返回参数的回调列表,参数类型为T,支持一对多
     17     /// </summary>
     18     public static Dictionary<string, List<Delegate>> events = new Dictionary<string, List<Delegate>>();
     19 
     20     /// <summary>
     21     /// 注册事件,1个返回参数
     22     /// </summary>
     23     /// <param name="eventName"></param>
     24     /// <param name="callback"></param>
     25     public static void AddEvent<T> (string eventName, Action<T> callback)
     26     {
     27         List<Delegate> actions = null;
     28 
     29         //eventName已存在
     30         if (events.TryGetValue(eventName, out actions))
     31         {
     32             actions.Add(callback);
     33         }
     34         //eventName不存在
     35         else
     36         {
     37             actions = new List<Delegate>();
     38 
     39             actions.Add(callback);
     40             events.Add(eventName ,actions);
     41         }
     42     }
     43 
     44     /// <summary>
     45     /// 注册事件,不带返回参数
     46     /// </summary>
     47     /// <param name="eventName"></param>
     48     /// <param name="callback"></param>
     49     public static void AddEvent(string eventName, Action callback)
     50     {
     51         List<Delegate> actions = null;
     52 
     53         //eventName已存在
     54         if (events.TryGetValue(eventName, out actions))
     55         {
     56             actions.Add(callback);
     57         }
     58         //eventName不存在
     59         else
     60         {
     61             actions = new List<Delegate>();
     62 
     63             actions.Add(callback);
     64             events.Add(eventName, actions);
     65         }
     66     }
     67 
     68     /// <summary>
     69     /// 移除事件
     70     /// </summary>
     71     /// <param name="eventName"></param>
     72     /// <param name="callback"></param>
     73     public static void RemoveEvent<T>(string eventName, Action<T> callback)
     74     {
     75         List<Delegate> actions = null;
     76 
     77         if (events.TryGetValue(eventName, out actions))
     78         {
     79             actions.Remove(callback);
     80             if (actions.Count == 0)
     81             {
     82                 events.Remove(eventName);
     83             }
     84         }
     85     }
     86     /// <summary>
     87     /// 移除全部事件
     88     /// </summary>
     89     public static void RemoveAllEvents ()
     90     {
     91         events.Clear();
     92     }
     93 
     94     /// <summary>
     95     /// 派发事件
     96     /// </summary>
     97     /// <param name="eventName"></param>
     98     /// <param name="arg"></param>
     99     public static void DispatchEvent<T>(string eventName, T arg)
    100     {
    101         List<Delegate> actions = null;
    102 
    103         if (events.ContainsKey(eventName))
    104         {
    105             events.TryGetValue(eventName, out actions);
    106 
    107             foreach (var act in actions)
    108             {
    109                 act.DynamicInvoke(arg);
    110             }
    111         }
    112     }
    113     /// <summary>
    114     /// 派发事件,不带参数
    115     /// </summary>
    116     /// <param name="eventName"></param>
    117     /// <param name="arg"></param>
    118     public static void DispatchEvent(string eventName)
    119     {
    120         List<Delegate> actions = null;
    121 
    122         if (events.ContainsKey(eventName))
    123         {
    124             events.TryGetValue(eventName, out actions);
    125 
    126             foreach (var act in actions)
    127             {
    128                 act.DynamicInvoke();
    129             }
    130         }
    131     }
    132 }
    EventManager.cs

    2、测试事件类

    2.1、先制作测试界面,包括两个接收(订阅)消息的Text组件,以及一个发布消息的Slider组件,层次结构见下图:

     预期效果:拖动Slider,Slider的值会同步显示到两个用于接收的Text组件上。

    2.2、编写测试类

     先写一个发布消息的类,在Slider的onValueChanged事件中执行发布操作,如下

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class Sender : MonoBehaviour
    {
        public Slider slider = null;
    
        private void Awake()
        {
            slider.onValueChanged.AddListener(delegate (float value) {
                Debug.LogFormat("slider:{0}", value);
                //有参分发
                EventManager.DispatchEvent<float>("NumberEvent", value);
    
                //无参分发
                EventManager.DispatchEvent("NumberEventNoParam");
            });
        }
    }
    Sender.cs

     再写一下接收消息的类,,如下

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class Receiver : MonoBehaviour
    {
        public Text receiveText1 = null;
        public Text receiveText2 = null;
    
        private void Awake()
        {
            //带参数回调
    
            //注册方法1
            EventManager.AddEvent<float>("NumberEvent", OnNumberChangeEventHandler);
    
            //注册方法2
            EventManager.AddEvent("NumberEvent", delegate (float arg) {
                receiveText2.text = (Convert.ToInt32(arg)).ToString();
            });
            
            //无参回调
            EventManager.AddEvent("NumberEventNoParam", delegate () {
                Debug.Log("无参回调");
            });
        }
    
        /// <summary>
        /// 事件处理方法
        /// </summary>
        /// <param name="arg"></param>
        private void OnNumberChangeEventHandler (float arg)
        {
            receiveText1.text = (Convert.ToInt32(arg)).ToString();
        }
    }
    Receiver.cs

     2.3、运行

    运行结果如下图:

    可以看到,Slider值的改变会立马同步到接收端Text中,实现了预期的功能。

     3、后记

    1、我在EventManager.cs中使用Action类型来接受事件的回调,而不是使用c#的delegate,是因为,Action是Unity已经定义好的一种公共delegate,使用起来更方便。

    2、目录的EventManager.cs只支持无参回调和一个参数的回调,如需更多参数回调,可以依照AddEvent<T>的写法,添加重载。

    3、在EventManager.cs中好像还缺一个无参的RemoveEvent方法,请自行补充。

     4、补充(2019-04-22)

    实际使用中发现,派发消息时,传递两个参数和三个参数的情况还是挺多的,因此对EventManager进行了补充,有些不优雅的地方也进行了修改:

      1 /**
      2 * UnityVersion: 2018.3.1f1
      3 * FileName:     EventManager.cs
      4 * Author:       TYQ
      5 * CreateTime:   2019/04/04 15:49:53
      6 * Description:  自定义的事件派发类
      7 */
      8 using System;
      9 using System.Collections;
     10 using System.Collections.Generic;
     11 using UnityEngine;
     12 
     13 public class EventManager
     14 {
     15     /// <summary>
     16     /// 带返回参数的回调列表,参数类型为T,支持一对多
     17     /// </summary>
     18     public static Dictionary<string, List<Delegate>> events = new Dictionary<string, List<Delegate>>();
     19 
     20     /// <summary>
     21     /// 通用注册事件方法
     22     /// </summary>
     23     /// <param name="eventName"></param>
     24     /// <param name="callback"></param>
     25     private static void CommonAdd (string eventName, Delegate callback)
     26     {
     27         List<Delegate> actions = null;
     28 
     29         //eventName已存在
     30         if (events.TryGetValue(eventName, out actions))
     31         {
     32             actions.Add(callback);
     33         }
     34         //eventName不存在
     35         else
     36         {
     37             actions = new List<Delegate>();
     38 
     39             actions.Add(callback);
     40             events.Add(eventName, actions);
     41         }
     42     }
     43 
     44     /// <summary>
     45     /// 注册事件,0个返回参数
     46     /// </summary>
     47     /// <param name="eventName"></param>
     48     /// <param name="callback"></param>
     49     public static void AddEvent(string eventName, Action callback)
     50     {
     51         CommonAdd(eventName, callback);
     52     }
     53 
     54     /// <summary>
     55     /// 注册事件,1个返回参数
     56     /// </summary>
     57     /// <param name="eventName"></param>
     58     /// <param name="callback"></param>
     59     public static void AddEvent<T> (string eventName, Action<T> callback)
     60     {
     61         CommonAdd(eventName, callback);
     62     }
     63     /// <summary>
     64     /// 注册事件,2个返回参数
     65     /// </summary>
     66     /// <param name="eventName"></param>
     67     /// <param name="callback"></param>
     68     public static void AddEvent<T, T1>(string eventName, Action<T, T1> callback)
     69     {
     70         CommonAdd(eventName, callback);
     71     }
     72     /// <summary>
     73     /// 注册事件,3个返回参数
     74     /// </summary>
     75     /// <param name="eventName"></param>
     76     /// <param name="callback"></param>
     77     public static void AddEvent<T, T1, T2>(string eventName, Action<T, T1, T2> callback)
     78     {
     79         CommonAdd(eventName, callback);
     80     }
     81 
     82     /// <summary>
     83     /// 通用移除事件的方法
     84     /// </summary>
     85     /// <param name="eventName"></param>
     86     /// <param name="callback"></param>
     87     private static void CommonRemove (string eventName, Delegate callback)
     88     {
     89         List<Delegate> actions = null;
     90 
     91         if (events.TryGetValue(eventName, out actions))
     92         {
     93             actions.Remove(callback);
     94             if (actions.Count == 0)
     95             {
     96                 events.Remove(eventName);
     97             }
     98         }
     99     }
    100 
    101     /// <summary>
    102     /// 移除事件 0参数
    103     /// </summary>
    104     /// <param name="eventName"></param>
    105     /// <param name="callback"></param>
    106     public static void RemoveEvent(string eventName, Action callback)
    107     {
    108         CommonRemove(eventName, callback);
    109     }
    110 
    111     /// <summary>
    112     /// 移除事件 1个参数
    113     /// </summary>
    114     /// <param name="eventName"></param>
    115     /// <param name="callback"></param>
    116     public static void RemoveEvent<T>(string eventName, Action<T> callback)
    117     {
    118         CommonRemove(eventName, callback);
    119     }
    120 
    121     /// <summary>
    122     /// 移除事件 2个参数
    123     /// </summary>
    124     /// <param name="eventName"></param>
    125     /// <param name="callback"></param>
    126     public static void RemoveEvent<T, T1>(string eventName, Action<T, T1> callback)
    127     {
    128         CommonRemove(eventName, callback);
    129     }
    130     /// <summary>
    131     /// 移除事件 3个参数
    132     /// </summary>
    133     /// <param name="eventName"></param>
    134     /// <param name="callback"></param>
    135     public static void RemoveEvent<T, T1, T2>(string eventName, Action<T, T1, T2> callback)
    136     {
    137         CommonRemove(eventName, callback);
    138     }
    139 
    140     /// <summary>
    141     /// 移除全部事件
    142     /// </summary>
    143     public static void RemoveAllEvents ()
    144     {
    145         events.Clear();
    146     }
    147 
    148     /// <summary>
    149     /// 派发事件,0参数
    150     /// </summary>
    151     /// <param name="eventName"></param>
    152     /// <param name="arg"></param>
    153     public static void DispatchEvent(string eventName)
    154     {
    155         List<Delegate> actions = null;
    156 
    157         if (events.ContainsKey(eventName))
    158         {
    159             events.TryGetValue(eventName, out actions);
    160 
    161             foreach (var act in actions)
    162             {
    163                 act.DynamicInvoke();
    164             }
    165         }
    166     }
    167 
    168     /// <summary>
    169     /// 派发事件 1个参数
    170     /// </summary>
    171     /// <param name="eventName"></param>
    172     /// <param name="arg"></param>
    173     public static void DispatchEvent<T>(string eventName, T arg)
    174     {
    175         List<Delegate> actions = null;
    176 
    177         if (events.ContainsKey(eventName))
    178         {
    179             events.TryGetValue(eventName, out actions);
    180 
    181             foreach (var act in actions)
    182             {
    183                 act.DynamicInvoke(arg);
    184             }
    185         }
    186     }
    187 
    188     /// <summary>
    189     /// 派发事件 2个参数
    190     /// </summary>
    191     /// <param name="eventName">事件名</param>
    192     /// <param name="arg">参数1</param>
    193     /// <param name="arg2">参数2</param>
    194     public static void DispatchEvent<T, T1>(string eventName, T arg, T1 arg2)
    195     {
    196         List<Delegate> actions = null;
    197 
    198         if (events.ContainsKey(eventName))
    199         {
    200             events.TryGetValue(eventName, out actions);
    201 
    202             foreach (var act in actions)
    203             {
    204                 act.DynamicInvoke(arg, arg2);
    205             }
    206         }
    207     }
    208 
    209     /// <summary>
    210     /// 派发事件 3个参数
    211     /// </summary>
    212     /// <param name="eventName">事件名</param>
    213     /// <param name="arg">参数1</param>
    214     /// <param name="arg2">参数2</param>
    215     /// <param name="arg3">参数3</param>
    216     public static void DispatchEvent<T1, T2, T3>(string eventName, T1 arg, T2 arg2, T3 arg3)
    217     {
    218         List<Delegate> actions = null;
    219 
    220         if (events.ContainsKey(eventName))
    221         {
    222             events.TryGetValue(eventName, out actions);
    223 
    224             foreach (var act in actions)
    225             {
    226                 act.DynamicInvoke(arg, arg2, arg3);
    227             }
    228         }
    229     }
    230 }
    EventManager
  • 相关阅读:
    5.16欢乐赛
    卢卡斯定理
    noip2017 宝藏
    [JSOI2009]计数问题
    HDU 1160 FatMouse's Speed
    HDU 1260 Tickets
    HDU 1176 免费馅饼
    HDU 1114 Piggy-Bank
    HDU 1074 Doing Homework
    HDU 1069 Monkey and Banana
  • 原文地址:https://www.cnblogs.com/imteach/p/10679239.html
Copyright © 2011-2022 走看看