unity遊戲框架學習-消息機制

概述:http://www.javashuo.com/article/p-nggymcxb-bw.htmlhtml

本篇咱們實現一種消息機制。爲何須要消息機制,很簡單,解耦合。c#

舉個例子,遊戲裏面當資源數量更新時(例如糧食+200),全部顯示該資源數量的界面都須要更新該資源的數量文本(例如訓練士兵、升級建築、治療、研發等等),這可能會涉及十幾種界面,而只有打開的界面須要更新。函數

那麼當客戶端收到服務端的數量更新消息時,在邏輯類裏一個個的判斷界面是否打開,若是界面打開則調用界面的更新方法顯然是很低效、耦合的。那麼消息機制的實現方式是怎麼樣的呢?lua

界面在打開時監聽一條事件RESOURCE_DATA_UPDATE,在關閉時移除該事件RESOURCE_DATA_UPDATE,邏輯類收到資源更新消息時會觸發這個事件,這樣每一個監聽該事件的界面均可以收到資源更新的通知。spa

具體的實現是經過事件類型EventType 和c#委託Delegate實現的,EventType是本身定義的枚舉。關鍵代碼以下所示指針

private static Dictionary<EventType, Delegate> m_EventTable = new Dictionary<EventType, Delegate>();
public delegate void CallBack();
//添加一個事件(例如界面打開時)
public static void AddListener(EventType eventType, CallBack callBack) { m_EventTable[eventType] = (CallBack)m_EventTable[eventType] + callBack; }
//移除一個事件(例如界面關閉時)
public static void RemoveListener(EventType eventType, CallBack callBack) { m_EventTable[eventType] = (CallBack)m_EventTable[eventType] - callBack; OnListenerRemoved(eventType); }
//觸發事件,邏輯類調用
public static void Broadcast(EventType eventType) { Delegate d; if (m_EventTable.TryGetValue(eventType, out d)) { CallBack callBack = d as CallBack; if (callBack != null) { callBack(); } else { throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具備不一樣的類型", eventType)); } } }

詳細代碼以下:code

 

1、c#端orm

1.回調定義:本來打算使用params object[] param做爲回調參數,這樣只須要定義一個回調。可是這種作法會致使頻繁的裝箱、拆箱操做,並且業務的代碼也很差寫。htm

裝箱:值類型轉引用類型,例如int裝object。對值類型進行裝箱會在堆中分配一個對象實例,並將該值複製到新的對象中。 最後會返回指向該內存的指針。咱們應該儘可能避免裝箱操做。對象

public delegate void CallBack();
public delegate void CallBack<T>(T arg);
public delegate void CallBack<T, X>(T arg1, X arg2);
public delegate void CallBack<T, X, Y>(T arg1, X arg2, Y arg3);
public delegate void CallBack<T, X, Y, Z>(T arg1, X arg2, Y arg3, Z arg4);
public delegate void CallBack<T, X, Y, Z, W>(T arg1, X arg2, Y arg3, Z arg4, W arg5);
//public delegate void CallBack(params object[] param);

2.事件定義:

public enum EventType
{
    ShowText,
}

3.事件添加、移除、分發:最多支持五個參數的回調

public class EventCenter
{
    private static Dictionary<EventType, Delegate> m_EventTable = new Dictionary<EventType, Delegate>();

    private static void OnListenerAdding(EventType eventType, Delegate callBack)
    {
        if (!m_EventTable.ContainsKey(eventType))
        {
            m_EventTable.Add(eventType, null);
        }
        Delegate d = m_EventTable[eventType];
        if (d != null && d.GetType() != callBack.GetType())
        {
            throw new Exception(string.Format("嘗試爲事件{0}添加不一樣類型的委託,當前事件所對應的委託是{1},要添加的委託類型爲{2}", eventType, d.GetType(), callBack.GetType()));
        }
    }
    private static void OnListenerRemoving(EventType eventType, Delegate callBack)
    {
        if (m_EventTable.ContainsKey(eventType))
        {
            Delegate d = m_EventTable[eventType];
            if (d == null)
            {
                throw new Exception(string.Format("移除監聽錯誤:事件{0}沒有對應的委託", eventType));
            }
            else if (d.GetType() != callBack.GetType())
            {
                throw new Exception(string.Format("移除監聽錯誤:嘗試爲事件{0}移除不一樣類型的委託,當前委託類型爲{1},要移除的委託類型爲{2}", eventType, d.GetType(), callBack.GetType()));
            }
        }
        else
        {
            throw new Exception(string.Format("移除監聽錯誤:沒有事件碼{0}", eventType));
        }
    }
    private static void OnListenerRemoved(EventType eventType)
    {
        if (m_EventTable[eventType] == null)
        {
            m_EventTable.Remove(eventType);
        }
    }
    //no parameters
    public static void AddListener(EventType eventType, CallBack callBack)
    {
        OnListenerAdding(eventType, callBack);
        m_EventTable[eventType] = (CallBack)m_EventTable[eventType] + callBack;
    }
    //Single parameters
    public static void AddListener<T>(EventType eventType, CallBack<T> callBack)
    {
        OnListenerAdding(eventType, callBack);
        m_EventTable[eventType] = (CallBack<T>)m_EventTable[eventType] + callBack;
    }
    //two parameters
    public static void AddListener<T, X>(EventType eventType, CallBack<T, X> callBack)
    {
        OnListenerAdding(eventType, callBack);
        m_EventTable[eventType] = (CallBack<T, X>)m_EventTable[eventType] + callBack;
    }
    //three parameters
    public static void AddListener<T, X, Y>(EventType eventType, CallBack<T, X, Y> callBack)
    {
        OnListenerAdding(eventType, callBack);
        m_EventTable[eventType] = (CallBack<T, X, Y>)m_EventTable[eventType] + callBack;
    }
    //four parameters
    public static void AddListener<T, X, Y, Z>(EventType eventType, CallBack<T, X, Y, Z> callBack)
    {
        OnListenerAdding(eventType, callBack);
        m_EventTable[eventType] = (CallBack<T, X, Y, Z>)m_EventTable[eventType] + callBack;
    }
    //five parameters
    public static void AddListener<T, X, Y, Z, W>(EventType eventType, CallBack<T, X, Y, Z, W> callBack)
    {
        OnListenerAdding(eventType, callBack);
        m_EventTable[eventType] = (CallBack<T, X, Y, Z, W>)m_EventTable[eventType] + callBack;
    }

    //no parameters
    public static void RemoveListener(EventType eventType, CallBack callBack)
    {
        OnListenerRemoving(eventType, callBack);
        m_EventTable[eventType] = (CallBack)m_EventTable[eventType] - callBack;
        OnListenerRemoved(eventType);
    }
    //single parameters
    public static void RemoveListener<T>(EventType eventType, CallBack<T> callBack)
    {
        OnListenerRemoving(eventType, callBack);
        m_EventTable[eventType] = (CallBack<T>)m_EventTable[eventType] - callBack;
        OnListenerRemoved(eventType);
    }
    //two parameters
    public static void RemoveListener<T, X>(EventType eventType, CallBack<T, X> callBack)
    {
        OnListenerRemoving(eventType, callBack);
        m_EventTable[eventType] = (CallBack<T, X>)m_EventTable[eventType] - callBack;
        OnListenerRemoved(eventType);
    }
    //three parameters
    public static void RemoveListener<T, X, Y>(EventType eventType, CallBack<T, X, Y> callBack)
    {
        OnListenerRemoving(eventType, callBack);
        m_EventTable[eventType] = (CallBack<T, X, Y>)m_EventTable[eventType] - callBack;
        OnListenerRemoved(eventType);
    }
    //four parameters
    public static void RemoveListener<T, X, Y, Z>(EventType eventType, CallBack<T, X, Y, Z> callBack)
    {
        OnListenerRemoving(eventType, callBack);
        m_EventTable[eventType] = (CallBack<T, X, Y, Z>)m_EventTable[eventType] - callBack;
        OnListenerRemoved(eventType);
    }
    //five parameters
    public static void RemoveListener<T, X, Y, Z, W>(EventType eventType, CallBack<T, X, Y, Z, W> callBack)
    {
        OnListenerRemoving(eventType, callBack);
        m_EventTable[eventType] = (CallBack<T, X, Y, Z, W>)m_EventTable[eventType] - callBack;
        OnListenerRemoved(eventType);
    }


    //no parameters
    public static void Broadcast(EventType eventType)
    {
        Delegate d;
        if (m_EventTable.TryGetValue(eventType, out d))
        {
            CallBack callBack = d as CallBack;
            if (callBack != null)
            {
                callBack();
            }
            else
            {
                throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具備不一樣的類型", eventType));
            }
        }
    }
    //single parameters
    public static void Broadcast<T>(EventType eventType, T arg)
    {
        Delegate d;
        if (m_EventTable.TryGetValue(eventType, out d))
        {
            CallBack<T> callBack = d as CallBack<T>;
            if (callBack != null)
            {
                callBack(arg);
            }
            else
            {
                throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具備不一樣的類型", eventType));
            }
        }
    }
    //two parameters
    public static void Broadcast<T, X>(EventType eventType, T arg1, X arg2)
    {
        Delegate d;
        if (m_EventTable.TryGetValue(eventType, out d))
        {
            CallBack<T, X> callBack = d as CallBack<T, X>;
            if (callBack != null)
            {
                callBack(arg1, arg2);
            }
            else
            {
                throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具備不一樣的類型", eventType));
            }
        }
    }
    //three parameters
    public static void Broadcast<T, X, Y>(EventType eventType, T arg1, X arg2, Y arg3)
    {
        Delegate d;
        if (m_EventTable.TryGetValue(eventType, out d))
        {
            CallBack<T, X, Y> callBack = d as CallBack<T, X, Y>;
            if (callBack != null)
            {
                callBack(arg1, arg2, arg3);
            }
            else
            {
                throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具備不一樣的類型", eventType));
            }
        }
    }
    //four parameters
    public static void Broadcast<T, X, Y, Z>(EventType eventType, T arg1, X arg2, Y arg3, Z arg4)
    {
        Delegate d;
        if (m_EventTable.TryGetValue(eventType, out d))
        {
            CallBack<T, X, Y, Z> callBack = d as CallBack<T, X, Y, Z>;
            if (callBack != null)
            {
                callBack(arg1, arg2, arg3, arg4);
            }
            else
            {
                throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具備不一樣的類型", eventType));
            }
        }
    }
    //five parameters
    public static void Broadcast<T, X, Y, Z, W>(EventType eventType, T arg1, X arg2, Y arg3, Z arg4, W arg5)
    {
        Delegate d;
        if (m_EventTable.TryGetValue(eventType, out d))
        {
            CallBack<T, X, Y, Z, W> callBack = d as CallBack<T, X, Y, Z, W>;
            if (callBack != null)
            {
                callBack(arg1, arg2, arg3, arg4, arg5);
            }
            else
            {
                throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具備不一樣的類型", eventType));
            }
        }
    }
}

 

2、lua端,lua對調並不須要定義。

1.事件定義:

local MESSAGE = 0
local function GetID()
    MESSAGE = MESSAGE + 1
    return MESSAGE
end

--消息table
GMsgDef                = {
    SOCKET_TCP_DATA                                        = GetID(),
}

2.事件處理:

local GameMsg={}

local Messagelisteners = {}
local GMsgDef = GMsgDef
local _msgPool = ObjectPool.new(function() return {} end, "GameMsgPool", false)

local function GetMsg(msgName)
    local msg = GMsgDef[msgName]
    if msg == nil then
        printErrorF("未定義MSG_{%s}", msgName)
    end

    return msg
end

-- msg 爲GMsgDef.XXX
-- target 由於調用類的成員須要target,若是backFunc不是類成員 則target傳nil;target也用來在實例Dispos的時候統一移除監聽,視狀況傳
-- backFunc 回調函數
function GameMsg.AddMessage(msgName, target, backFunc)
    if not msgName then
        return
    end
    local msg = GetMsg(msgName)

    --操做
    local lstListeners = Messagelisteners[msg]
    if lstListeners == nil then
        lstListeners = {}
        Messagelisteners[msg] = lstListeners
    end
    if GameMsg.HasMessageAlready(msg, target, backFunc) then
        printWarning("GameMsg.AddMessage duplicate, ", msgName)
        return
    end
    local msgObj = _msgPool:Pop()
    msgObj.target = target
    msgObj.func = backFunc
    table.insert(lstListeners, msgObj)
end

-- 傳入監聽消息列表
function GameMsg.AddMessageList(msglist, target, backFunc)
    if not msglist then
        return
    end
    for _,v in ipairs(msglist) do
        GameMsg.AddMessage(v, target, backFunc)
    end
end

function GameMsg.SendMessage(msgName, ...)
    -- target
    local msg = GetMsg(msgName)
    local msgs = Messagelisteners[msg]
    if msgs == nil then
        return
    end
    for i = #msgs, 1, - 1 do
        local l = msgs[i]
        if l then
            local func = l["func"]
            local target = l["target"]
            if func then
                if target then
                    func(target, ...)
                else
                    func(...)
                end
            end
        end
    end
end

-- 對象和方法都有相應映射的話就返回
function GameMsg.HasMessageAlready(msg, target, func)
    local msgs = Messagelisteners[msg]
    if not msgs then
        return false
    end
    for k, v in ipairs(msgs) do
        if v["func"] == func and v.target==target then
            return true
        end
    end
    return false
end

-- 移除某個消息監聽
function GameMsg.RemoveMessage(msgName, target, func)
    local msg = GetMsg(msgName)
    local msgs = Messagelisteners[msg]
    if not msgs then
        return
    end
    for i=#msgs,1,-1 do
        local item = msgs[i]
        if item["func"] == func and item.target==target then
            _msgPool:Push(msgs[i])
            table.remove(msgs, i)
        end
    end
    return false
end


function GameMsg.RemoveMessageByTarget(target)
    for k,v in pairs(Messagelisteners) do
        for i=#v,1,-1 do
            if v[i]["target"] == target then
                _msgPool:Push(v[i])
                table.remove(v, i)
            end
        end
    end
end

function GameMsg.RemoveMessageFromTargetByName(target, msgName)
    --操做
    local msg = GetMsg(msgName)
    local msgs = Messagelisteners[msg]
    if msgs then
        for i=#msgs,1,-1 do
            if msgs[i]["target"] == target then
                _msgPool:Push(msgs[i])
                table.remove(msgs, i)
            end
        end
    end
end

function GameMsg.RemoveAllMessage()
    Messagelisteners = {}
end

return GameMsg
相關文章
相關標籤/搜索