unity游戏框架学习-消息机制
概述:https://www.cnblogs.com/wang-jin-fu/p/10975660.html
本篇我们实现一种消息机制。为什么需要消息机制,很简单,解耦合。
举个例子,游戏里面当资源数量更新时(例如粮食+200),所有显示该资源数量的界面都需要更新该资源的数量文本(例如训练士兵、升级建筑、治疗、研发等等),这可能会涉及十几种界面,而只有打开的界面需要更新。
那么当客户端收到服务端的数量更新消息时,在逻辑类里一个个的判断界面是否打开,如果界面打开则调用界面的更新方法显然是很低效、耦合的。那么消息机制的实现方式是怎么样的呢?
界面在打开时监听一条事件RESOURCE_DATA_UPDATE,在关闭时移除该事件RESOURCE_DATA_UPDATE,逻辑类收到资源更新消息时会触发这个事件,这样每个监听该事件的界面都可以收到资源更新的通知。
具体的实现是通过事件类型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)); } } }
详细代码如下:
一、c#端
1.回调定义:原本打算使用params object[] param作为回调参数,这样只需要定义一个回调。但是这种做法会导致频繁的装箱、拆箱操作,而且业务的代码也不好写。
装箱:值类型转引用类型,例如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)); } } } }
二、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
一直想把之前工作、学习时记录的文档整理到博客上,一方面温故而知新,一方面和大家一起学习 -程序小白