Unity中消息派发系统的总结(1)

消息派发对项目中各个功能模块的解耦非常有帮助,免去模块间复杂的调用.

但unity自带的消息函数SendMessage效率一直都不高,所以基本上项目中都很少用它,而是自己写一套消息派发.

 

消息派发原理简单:订阅发布模式.

各模块需要注意哪些消息就订阅哪些消息[比如我定了报纸,而我邻居定了牛奶]

消息派发者会保存对应的订阅者信息[这个消息派发企业业务很广泛,既有报纸也有牛奶]

业务逻辑中需要触发该消息时,消息派发者就会检索我的订阅信息看看有没有模块订阅了该消息,有那么触发这个模块的处理函数[派出快递员,敲你大门,把报纸牛奶给你] 

 

实现方式大致有2种:

1.

->写消息派发类,该类作为单例确保全局调用,它包含 订阅函数 取消订阅函数 和 派发函数 . 以及存储订阅信息的数据结构:Dictionary>

->写一个接口类, 如 IMsgHandler , 它声明 订阅者模块处理消息的函数格式. 如: public bool OnMsgHandler(string msgName,param object[] args);

->订阅者继承接口类,订阅消息并重写IMsgHandler接口实现自己的业务逻辑 .

 

2.

->写消息派发类,该类作为单例确保全局调用,它包含 订阅函数 取消订阅函数 和 派发函数 . 以及存储订阅信息的数据结构Dictionary>

->订阅者继承接口类,注册订阅消息函数,实现自己的业务逻辑 .

 

这两种方式的区别在于第一种存储的是对象 ,另一种存储的是函数引用.

存储对象:派发时 遍历字典中订阅的对象,然后调用消息处理接口函数,所有的逻辑都在一个函数中 比较集中. 

存储函数引用:派发时 遍历字典中订阅的函数引用,直接调用该引用, 调用相对分散但方便

 

下面贴代码:

第一种:

接口类[IMsgReceiver.cs]

public interfaceIMsgReceiver{
    // msgName:消息名 , args:参数 , 注意返回值 如果返true 表示该消息已经被截断 消息派发不会再向下派发
    bool OnMsgHandler(string msgName,params object[] args);
}

 

 

消息派发类(单例)[MsgDispatcher.cs]

public class MsgDispatcher{
    private Dictionary> m_Msgs = new Dictionary>();  
    
    private static MsgDispatcher m_Ins;
    public static MsgDispatcher GetInstance(){
            if(m_Ins==null){
                m_Ins = new MsgDispatcher();
            }
            return m_Ins;
        }


        //订阅消息
        public void Subscribe(string msg, IMsgReceiver recevier) {
            if (recevier == null) {
                Debug.LogError("SubscribeMsg : recevier == null");
                return;
            }

            if (Check(msg, recevier)) {
                Debug.LogFormat("SubscribeMsg: recevier has been subscribed ,msg={0},recevier={1}", msg, recevier.ToString());
            } else {
                if (this.m_Msgs.ContainsKey(msg)) {
                    this.m_Msgs[msg].Add(recevier);
                } else {
                    List list = new List();
                    list.Add(recevier);
                    this.m_Msgs.Add(msg, list);
                }
            }
        }

        // 取消订阅消息
        public void UnSubscribe(string msg, IMsgReceiver recevier) {
            if (recevier == null) {
                Debug.LogError("UnSubscribeMsg: recevier == null");
                return;
            }

            if (Check(msg, recevier)) {
                this.m_Msgs[msg].Remove(recevier);
            }
        }

        public void UnSubscribe(IMsgReceiver recevier) {
            if(recevier == null) {
                Debug.LogError("UnSubscribeMsg: recevier == null");
                return;
            }
            foreach(var iter in m_Msgs) {
                iter.Value.Remove(recevier);
            }
        }

        //检查订阅
        public bool Check(string msg, IMsgReceiver recevier) {
            if (m_Msgs.ContainsKey(msg)) {
                var list = m_Msgs[msg];
                return list.Contains(recevier);
            }
            return false;
        }

        //清除
        public void ClearAll() {
            m_Msgs.Clear();
        }

        //抛出消息
        public void Fire(string msg, params object[] args) {
            if (!this.m_Msgs.ContainsKey(msg)) {
                Debug.LogWarning("Fire msg: msg has no receiver!");
                return;
            }

            Debug.Log("[MsgDispatcher] fire msg:"+msg);

            List list = this.m_Msgs[msg];
            try {
                for (int i = 0; i < list.Count; ++i) {
                    if (list[i] != null) {
                        bool bNext = list[i].OnMsgHandler(msg, args);
                        if (bNext) //有返回true的 消息会被截断,下面的handler不会接受到该消息
                            {
                            Debug.LogWarningFormat("Fire msg: msg[{0}] has been stop fire!", msg);
                            break;
                        }
                    }
                }
            } catch {

            }
        }
  
}

 

最后来看看业务逻辑类如何使用.

比如 玩家类(Player.cs) 他需要订阅牛奶消息(Msg_Milk)

先声明牛奶的消息[GameEvents.cs]

public static class GameEvents{
    public const string Msg_Milk = "Msg_Milk";    //牛奶消息
}

 

 

玩家类[Player.cs]

public class Player:MonoBehaviour,IMsgReceiver{
    
    //订阅牛奶消息
    void OnEnable(){
        MsgDispatcher.GetInstance().Subscribe(GameEvents.Msg_Milk,this);
    }
    
    //取消订阅牛奶消息
    void OnDisable(){
        MsgDispatcher.GetInstance().UnSubscribe(GameEvents.Msg_Milk,this);
    }

    //重写消息处理函数
    public bool OnMsgHandler(string msgName,params object[] args){
            switch(msgName){
                case GameEvents.Msg_Milk:
                        return onMsgMilk((string)args[0]);
            }
            return false;
        }
        
        // 真正处理milk消息的函数
        bool onMsgMilk(string content){
               Debug.Log("Player收到milk消息,参数:"+content);
                return false; 
        }
}

 

 

最后的最后写一个测试类3秒后触发牛奶消息[Test.cs]

public class Test:MonoBehaviour{
    void Awake(){{
        StartCoroutine(triggerMsg());
    }

    IEnumerator triggerMsg(){
    yield return new WaitForSeconds(3f);
    //根据你的业务逻辑传入不同参数
    MsgDispatcher.GetInstance().Fire(GameEvents.Msg_Milk,"哈哈哈哈");    
  }
}

 

 

 

 

 

 

 

你可能感兴趣的:(unity)