消息处理机制(一)

来源:互联网 发布:float定义数组 编辑:程序博客网 时间:2024/06/11 09:22

                                                                                                   消息处理机制

从中国移动的电话交流开始说起吧,先通过下面一个图了解它们消息交互的原理

 

 信息交流有两种方式:

第一种:在同一个基站,不通过服务器

第二种:在不同基站,必须通过服务器转发。

 

在我们的游戏中也可以按照这个原理设计一个消息处理机制

1. 第一种方式 不通过服务器

Msg消息----------->对应的Manager--------------->对应的Script

第二种方式  通过服务器

Msg消息--------对应的Manager---------->服务器------------>对应的Manager----------->对应的script

下面是原理图:


 

考虑到现在大部分游戏都用到热更新技术,所以Lua版的消息处理框架也是类似,那么C#Lua的交互就是两个消息处理中心去做对接的。

 

我们这里的核心是MsgCenter,那么它的职责主要做什么呢?(要实现这个机制,涉及到其他一些模块和底层的实现,后面会慢慢提及)

1.怎么样找到对应的Manager(借用计算机网络的网络七层协议中的网络层的原理,SocketIP 192.168.0.1 ---192标记标志你在哪个网段上,168表示你在哪个区域,0表示局域网下的哪个区域。受IP和端口号的启发。100)

下面是公网下不同终端的消息交互的原理图:

 

 

 

 

 

 

借鉴这个原理,我让每个Manger处理3000条消息,这在游戏中已经够用了。

然后呢我定义了一个枚举,通过这个枚举把每个模块的处理消息区间大致抽象出来,每个Manager分别对应着自己的ManagerId

 

 

 

定义一个消息ID 3003,怎么样找到对应的UIManager

通过下面的GetManager()方法,这里涉及到一个数学公式,就不多做说明了。

 

2.怎么样在Manager中找到对应的script?

 

这里我先说一下Manger的功能

a. 存储对应的注册进来的msg

b. msg来了以后找到对应的脚本。

要满足这两种条件的数据结构是什么?

 

首先考虑的是第一种数据结构Dictionary<ushort,MonoBehaviour> 但是这种方式只能满足一个mono脚本对应一个msg,

那么如果有多个mono脚本,对应一个msg,比如说多个脚本(不仅仅是manager,还有它管理的script)要监听一个消息msg,

那么一个mono脚本可以注册多条消息,这种方式就满足不了需求,所以这里我们采用另一种数据结构字典套链表

Dictionary<ushort,EventNode>

链表:

 

 

那么我们在C#中怎么实现这种数据链路?

这里我借助了节点,定义节点类,然后里面有个数据域和指针域。

如下:

涉及到的其他类

 

节点类

 

 因为每个manager里面都会有存储消息,注册消息等相关功能,所以我把它们提取在ManagerBase父类里面,其他Mnager只需要继承就可以了。

/// <summary>

/// Manager的基类

/// </summary>

public class ManagerBase :MonoBase{

 

    注意:这里之所以用ushort,是因为每个msg都有一个msgId,我们可以根据msgId去匹配相应的msg对象,所以我们这个字典可以理解为以msgId作键,以可以监听这条消息的脚本链表作值。这样我们就可以做到一个消息msg对应多个mono脚本,那么一个脚本呢,也可以包含多个消息msg。用数据结构来说,就是可以做到多对多。

 

    /// <summary>

    /// 存储注册消息

    /// </summary>

    public Dictionary<ushort,EventNode> eventTree = new Dictionary<ushort,EventNode>();

    

   

 

    /// <summary>

    /// 往一个脚本里注册若干消息

    /// </summary>

    /// <param name="mono">要注册的脚本</param>

    /// <param name="msgs">一个脚本可以注册多条消息数组里面的值就是每个消息的msgId</param>

    public void RegisterMsg(MonoBase mono,params ushort[] msgs )

    {

        for (int i = 0; i < msgs.Length; i++)

        {

            EventNode node = new EventNode(mono);

 

            RegisterMsg(msgs[i], node);

        }

    }

 

    /// <summary>

    /// 注册一个消息链表

    /// </summary>

    /// <param name="msgId">根据msgId</param>

    /// <param name="data">node链表</param>

    public void RegisterMsg(ushort msgId,EventNode node)

    {

        if (!eventTree.ContainsKey(msgId))

        {

            eventTree.Add(msgId, node);ss

        }

        else

        {

            EventNode temp = eventTree[msg];

            //如果当前键对应的空间不止一个节点

            //那么遍历节点,找到最后为空的那个节点,

 

            while (temp.next != null)

            {

              temp=temp.next;

            }

            //把node添加在最后

            temp.next = node;

        }

    }

 

    /// <summary>

    /// 去掉一个脚本的若干个消息

    /// </summary>

    /// <param name="mono"></param>

    /// <param name="msgs"></param>

    public void UnRegisterMsg(MonoBase mono,params ushort[] msgs)

    {

        for (int i = 0; i < msgs.Length; i++)

        {

            UnRegisterMsg(msgs[i], mono);

        }

    }

 

    /// <summary>

    /// 去掉一个消息链表

    /// </summary>

    /// <param name="msgId"></param>

    /// <param name="node"></param>

    public void UnRegisterMsg(ushort msgId,MonoBase node)

    {

        if (!eventTree.ContainsKey(msgId))

        {

            Debug.LogWarning("not contail id==" + msg);

            return;

        }

        else

        {

            EventNode temp = eventTree[msgId];

            //去掉头部,包含两种情况

            if (temp.data == node)

            {

                EventNode header = temp;

                //后面多个节点

                if (header.next != null)

                {

                    header.data = temp.next.data;

                    header.next = temp.next.next;

                }

                else //只有一个节点的情况

                {

                    eventTree.Remove(msgId);

                }

            }

            else //去掉尾部和中间的节点

            {

                while (temp.next != null && temp.next.data != null)

                {

                    temp = temp.next;

                } //表示已经找到该节点

                //没有引用 ,自动释放

                if (temp.next.next != null)  //去掉中间

                {

                    temp.next = temp.next.next;

                }

                else //去掉尾部

                {

                    temp.next = null;

                }

            }

        }

    }

 

    /// <summary>

    /// 来了消息,通知整个消息链表

    /// </summary>

    /// <param name="msg"></param>

    public override void ProcessEvent(MsgBase msg)

    {

        if (!eventTree.ContainsKey(msg.msgId))

        {

            //方便找错

            Debug.LogError("not contains msgid==" + msg.msgId);

            Debug.LogError("Msg Manager==" + msg.GetManager());

 

            return;

        }

        else

        {

            EventNode temp = eventTree[msg.msgId];

            //通过key 找到链表,然后全部通知

            do

            {

               if (temp.next != null && temp.next.data != null)

                {

                    temp = temp.next;

                }

                //策略模式 消息自己处理

                temp.data.ProcessEvent(msg);

            } while (temp!=null);

        }

 

    }

}

 

这里涉及到一个知识点:链表中移除值的时候,有一下三种情况:

1. 在尾部 怎么移除

2. 在中间 怎么移除

3. 在头部怎么移除

那么这里怎么处理的我在UnRegisterMsg()方法里面做了处理。


原创粉丝点击