概述: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