本文首發自inspoy的雜七雜八 | 菜雞inspoy的學習記錄
前言
之前一直在用cocos2d-x用c++寫程式碼嘛,所以為了開發方便自己設計了一套事件系統,現在轉到unity用c#了,就很自然地把之前那套東西搬過來用了XD
C#自帶的event其實就完全可以用,不過功能略顯簡陋,Unity自帶的EventSystem感覺太複雜了,而且我又不太喜歡去操作editor,所以我還是決定自己造輪子,弄一個最適合自己的事件機制
設計
事件機制基於觀察者模式,事件訂閱者向某個事件釋出者訂閱事件,繫結回撥函式,事件釋出者釋出事件時會通知所有訂閱者,按訂閱順序依次呼叫他們預留的回撥函式
三個類,SFEvent
(SF是字首,啥意思大家也不用在意233),SFEventData
,SFEventDispatcher
類 | 作用 |
---|---|
SFEvent | 事件類 |
SFEventData | 事件所包含的資料 |
SFEventDispatcher | 事件釋出者 |
SFEvent
其中,事件類SFEvent
包含型別,事件資料SFEventData
,以及釋出者
不同事件的資料可能需要不同的資料結構,所以我們抽象出一層介面ISFEventData
,實際使用的資料結構均繼承這個介面即可
下面貼出部分程式碼(不完整,完整程式碼見本文末尾)
public class SFEvent
{
public string eventType; // 事件型別
public ISFEventData data; // 事件包含的自定義資料
public object target; // 事件傳送者
// 以下是事件列舉
public const string EVENT_TEST = "EVENT_TEST";
};
public interface ISFEventData
{
};複製程式碼
SFEventData
所有事件資料的資料結構均繼承了ISFEventData
這個空介面,這樣一來不同的事件就可以定製自己需要的事件資料結構了,如果時間資料非常簡單比如只有一個數字,我這裡也準備了一個通用的資料結構:
public class SFSimpleEventData : ISFEventData
{
public object objVal = null;
public int intVal = 0;
public float floatVal = 0;
public string strVal = "";
}複製程式碼
SFEventDispatcher
這個事件系統的核心部分,負責分發事件,管理訂閱
之前用cocos的時候習慣使用多繼承的方式來把dispatcher的功能加到一個類上,不過現在C#不支援多繼承了,於是就只能使用組合的方式,在類中儲存一個dispatcher類的例項來實現類似的效果。寫法會從this->dispatchEvent(e);
變成this.dispatcher.dispatchEvent(e);
使用C#的委託來實現訂閱,訂閱者傳遞這個委託型別的回撥函式給釋出者即可:
public delegate void SFListenerSelector(SFEvent e);複製程式碼
使用一個字典來管理不同的事件所各自對應的訂閱者
Dictionary<string, List<SFListenerSelector> > m_dictListener;複製程式碼
訂閱者通過addEventListener
方法來訂閱一個事件:
public bool addEventListener(string eventType, SFListenerSelector sel)
{
if (eventType != "" && sel != null) // 判斷有效性
{
if (hasEventListener(eventType, sel))
{
// SFUtils這個類是我自己弄的,下一篇文章再講
SFUtils.log(string.Format("重複監聽!type={0}", eventType));
}
if (!m_dictListeners.ContainsKey(eventType))
{
// 不存在的話就新建一個
List<SFListenerSelector> newSelectors = new List<SFListenerSelector>();
m_dictListeners[eventType] = newSelectors;
}
var selectors = m_dictListeners[eventType];
selectors.Add(sel);
return true;
}
return false;
}複製程式碼
任何人都可以通過釋出者(通常來說是釋出者自己)來發布事件:
public int dispatchEvent(SFEvent e)
{
int count = 0;
if (m_dictListeners.ContainsKey(e.eventType))
{
var selectors = m_dictListeners[e.eventType];
foreach (var item in selectors)
{
item(e);
count += 1;
}
}
return count;
}複製程式碼
用途相關
其實更常見的使用場景是在UI,我這次用的是Unity5自帶的UGUI,寫了一個控制元件來結合Unity的事件系統和我這個事件系統,實際開發的時候使用UI事件的方式和使用普通事件的方式是完全一致的,這個我們後面再詳細介紹
完整程式碼
上面貼出的程式碼片段由於篇幅限制只保留了關鍵部分,完整的程式碼可在我的github上找到