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方法,请自行补充。

 

人已赞赏
随笔日记

tragicservers:稳定建站的OpenVZ,年付$15,有高配版,洛杉矶机房

2020-11-9 4:20:05

随笔日记

【JVM虚拟机】(7)---深入理解Class中-属性集合

2020-11-9 4:20:07

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索