[计算机] Unity 游戏框架搭建 (五) 简易消息机制

查看:503 |回复:1 | 2016-9-29 17:40:12

您需要 登录 才可以下载或查看,没有账号?注册

x
本帖最后由 胖纸_DHW 于 2020-12-23 18:39 编辑

什么是消息机制?

108753-160R2154343a9.jpg

108753-160R215435K64.jpg





为什么用消息机制?


三个字,解!!!!耦!!!!合!!!!。


我的框架中的消息机制用例:


1.接收者


using UnityEngine;

using System.Collections;


using QFramework;


/// <summary>

/// 1.接收者需要实现IMsgReceiver接口。

/// 2.使用this.RegisterLogicMsg注册消息和回调函数。

/// </summary>

public class Receiver : MonoBehaviour,IMsgReceiver {


    void Awake()

    {

        this.RegisterLogicMsg ("Receiver Show Sth", ReceiverMsg);


//        this.UnRegisterLogicMsg ("Receiver Show Sth", ReceiverMsg);


    }



    void ReceiverMsg(params object[] paramList)

    {

        foreach (var sth in paramList) {

            QPrint.Warn (sth.ToString());

        }

    }

}

2.发送者


using UnityEngine;

using System.Collections;

using QFramework;


/// <summary>

/// 1.发送者需要,实现IMsgSender接口

/// 2.调用this.SendLogicMsg发送Receiver Show Sth消息,并传入两个参数

/// </summary>

public class Sender : MonoBehaviour,IMsgSender {


    // Update is called once per frame

    void Update () {

        this.SendLogicMsg ("Receiver Show Sth","你好","世界");

    }

}

3.运行结果


使用起来几行代码的事情,实现起来就没这么简单了。


如何实现的?

&#8195;&#8195;可以看到接收者实现了接口IMsgReceiver,发送者实现了接口IMsgSender。 那先看下这两个接口定义。

IMsgReceiver:


using UnityEngine;

using System.Collections;


namespace QFramework {


    public interface IMsgReceiver  {



    }

}

IMsgSender


using UnityEngine;

using System.Collections;


namespace QFramework {


    public interface IMsgSender  {


    }

}

毛都没有啊。也没有SendLogicMsg或者ReceiveLogicMsg方法的定义啊。


答案是使用C# this的扩展方式实现接口方法。 不清楚的童鞋请百度C# this扩展,有好多文章就不介绍了。 以上先告一段落,先介绍个重要的角色,MsgDispatcher(消息分发器)。


贴上第一部分代码:


namespace QFramework {

    /// <summary>

    /// 消息分发器

    /// C# this扩展 需要静态类

    /// </summary>

    public static class QMsgDispatcher  {


        /// <summary>

        /// 消息捕捉器

        /// </summary>

        class LogicMsgHandler {


            public IMsgReceiver receiver;

            public  VoidDelegate.WithParams callback;


            /*

             * VoidDelegate.WithParams 是一种委托 ,定义是这样的

             *

             *  public class VoidDelegate{

             *      public delegate void WithParams(params object[] paramList);

             *  }

             */

            public LogicMsgHandler(IMsgReceiver receiver,VoidDelegate.WithParams callback)

            {

                this.receiver = receiver;

                this.callback = callback;

            }

        }


        /// <summary>

        /// 每个消息名字维护一组消息捕捉器。

        /// </summary>

        static Dictionary<string,List<LogicMsgHandler>> mMsgHandlerDict = new Dictionary<string,List<LogicMsgHandler>> ();

读注释!!!


贴上注册消息的代码


        /// <summary>

        /// 注册消息,

        /// 注意第一个参数,使用了C# this的扩展,

        /// 所以只有实现IMsgReceiver的对象才能调用此方法

        /// </summary>

        public static void RegisterLogicMsg(this IMsgReceiver self, string msgName,VoidDelegate.WithParams callback)

        {

            // 略过

            if (string.IsNullOrEmpty(msgName)) {

                QPrint.FrameworkWarn("RegisterMsg:" + msgName + " is Null or Empty");

                return;

            }


            // 略过

            if (null == callback) {

                QPrint.FrameworkWarn ("RegisterMsg:" + msgName + " callback is Null");

                return;

            }


            // 略过

            if (!mMsgHandlerDict.ContainsKey (msgName)) {

                mMsgHandlerDict [msgName] = new List<LogicMsgHandler> ();

            }


            // 看下这里

            var handlers = mMsgHandlerDict [msgName];


            // 略过

            // 防止重复注册

            foreach (var handler in handlers) {

                if (handler.receiver == self && handler.callback == callback) {

                    QPrint.FrameworkWarn ("RegisterMsg:" + msgName + " ayready Register");

                    return;

                }

            }


            // 再看下这里

            handlers.Add (new LogicMsgHandler (self, callback));

        }



发送消息相关的代码


        /// <summary>

        /// 发送消息

        /// 注意第一个参数

        /// </summary>

        public static void SendLogicMsg(this IMsgSender sender, string msgName,params object[] paramList )

        {

            // 略过,不用看

            if (string.IsNullOrEmpty(msgName)) {

                QPrint.FrameworkError("SendMsg is Null or Empty");

                return;

            }


            // 略过,不用看

            if (!mMsgHandlerDict.ContainsKey(msgName)){

                QPrint.FrameworkWarn("SendMsg is UnRegister");

                return;

            }


            // 开始看!!!!

            var handlers = mMsgHandlerDict[msgName];



            var handlerCount = handlers.Count;


            // 之所以是从后向前遍历,是因为  从前向后遍历删除后索引值会不断变化

            // 参考文章,http://www.2cto.com/kf/201312/266723.html

            for (int index = handlerCount - 1;index >= 0;index--)

            {

                var handler = handlers[index];


                if (handler.receiver != null) {

                    QPrint.FrameworkLog ("SendLogicMsg:" + msgName + " Succeed");

                    handler.callback (paramList);

                } else {

                    handlers.Remove (handler);

                }

            }

        }

OK主要的部分全都贴出来啦

以上代码以全部上传到论坛上边



可以改进的地方:


&#8195;&#8195;1.目前整个游戏的消息都由一个字典维护,可以改进为每个模块维护一个字典或者其他方式。 &#8195;&#8195;2.消息名字类型由字符串定义的,可以改成枚举转unsigned int方式。 &#8195;&#8195;3.欢迎补充。




&#8195;&#8195;1.如果是MonoBehaviour注册消息之后,GameObject Destroy之前一定要注销消息,之前的解决方案是,自定义一个基类来维护该对象已经注册的消息列表,然后在基类的OnDestory时候遍历卸载。 &#8195;&#8195;2.欢迎补充。

评分

参与人数 1元素币 +5 展开 理由
狼之独步 + 5 凉鞋大大的文章真是不错

查看全部评分

2016-9-29 17:40:12  
 赞 赞 0

使用道具 登录

1个回答,把该问题分享到群,邀请大神一起回答。

CG 游戏行业专业问题

用户体验交互综合文库
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表