Unity3D中利用Action實現本身的消息管理(訂閱/發佈)類

引言

通常的軟件開發過程當中,爲了方便對項目進行管理、維護和擴展,一般會採用一種MVC框架,以將顯示邏輯、業務邏輯和數據進行分離。c#

這在傳統企業軟件的開發中很常見,但我在使用Unity作遊戲開發的時候卻幾乎找不到相關框架。框架

其緣由猜想大概有兩點,一是遊戲開發模式多變,不一樣類型的遊戲代碼結構差別很大,很難有一個適用性很強的框架出現;二是Unity太年輕,其大範圍使用也不過是最近三四年的事情。ide

沒有框架也不是意味着沒有辦法,MVC只是一種規範,只要在開發過程當中對代碼的組織結構及用途作必定的約束,就能達到各層分離的效果。測試

在代碼分層組織的結構中,出於解耦合的需求,一般須要一個對事件/消息進行管理的類,以便在各層之間傳送消息,其功能包括事件/消息的訂閱、發佈以及取消訂閱。spa

本文不會寫怎麼實現一個MVC結構(駕馭不了),只說說這個事件管理類的實現方法。3d

事件管理類的實現

一、編寫EventManager類

一個事件管理類經過包括三個功能,訂閱、發佈消息、取消訂閱,對應到代碼中,也是就三個方法:AddEvent、DispatchEvent、RemoveEvent,還有一個字典List,對訂閱事件作管理,實現以下:code

 

  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.一、先製做測試界面,包括兩個接收(訂閱)消息的Text組件,以及一個發佈消息的Slider組件,層次結構見下圖:orm

 預期效果:拖動Slider,Slider的值會同步顯示到兩個用於接收的Text組件上。blog

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.三、運行

運行結果以下圖:

能夠看到,Slider值的改變會立馬同步到接收端Text中,實現了預期的功能。

 

 三、後記

一、我在EventManager.cs中使用Action類型來接受事件的回調,而不是使用c#的delegate,是由於,Action是Unity已經定義好的一種公共delegate,使用起來更方便。

二、目錄的EventManager.cs只支持無參回調和一個參數的回調,如需更多參數回調,能夠依照AddEvent<T>的寫法,添加劇載。

三、在EventManager.cs中好像還缺一個無參的RemoveEvent方法,請自行補充。

 

 四、補充(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
相關文章
相關標籤/搜索