用Unity做個遊戲(二) – 事件系統

Inspoy Cheng發表於2019-02-28

本文首發自inspoy的雜七雜八 | 菜雞inspoy的學習記錄

前言

之前一直在用cocos2d-x用c++寫程式碼嘛,所以為了開發方便自己設計了一套事件系統,現在轉到unity用c#了,就很自然地把之前那套東西搬過來用了XD
C#自帶的event其實就完全可以用,不過功能略顯簡陋,Unity自帶的EventSystem感覺太複雜了,而且我又不太喜歡去操作editor,所以我還是決定自己造輪子,弄一個最適合自己的事件機制

設計

事件機制基於觀察者模式,事件訂閱者向某個事件釋出者訂閱事件,繫結回撥函式,事件釋出者釋出事件時會通知所有訂閱者,按訂閱順序依次呼叫他們預留的回撥函式
三個類,SFEvent(SF是字首,啥意思大家也不用在意233),SFEventDataSFEventDispatcher

作用
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上找到

相關文章