• 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
  • 相关阅读:
    Oracle 多表查询优化
    FZU 2150 Fire Game
    HDU 1058 Humble Numbers
    UVA 11624 Fire!
    POJ 1321 棋盘问题
    线性表的基本操作
    POJ 3414 Pots(广搜输出路径)
    HDU 1495 非常可乐
    POJ 1847 Tram
    POJ 3159 Candies(查分约束)
  • 原文地址:https://www.cnblogs.com/wang-jin-fu/p/11255831.html
Copyright © 2020-2023  润新知