Unity之基于观察者模式的消息分发机制

1、前言

目前脚本之间的交互有下面三种常用方式:

  • 通过GetComponent<脚本>().Method();
  • 单例模式传递数据
  • 脚本组件的SendMessage方法

这三种方式,耦合性都很高;第三种方式由于是通过反射实现的,所以效率很低,还存在很多隐患,因为不知道哪一天,新接手这代码的程序员就可能把那个方法删了而产生新的bug。

2、消息分发机制

下面通过委托与事件来实现一个消息分发功能

using System.Collections.Generic;

public class MsgHandler
{

    public delegate void DelMsgHandler(Msg msg);

    private static Dictionary<string, DelMsgHandler> mDicMsgs = new Dictionary<string, DelMsgHandler>();

    //添加监听者
    public static void AddListener(string msgType, DelMsgHandler handler)
    {
        //判空
        if (mDicMsgs == null) mDicMsgs = new Dictionary<string, DelMsgHandler>();
        if (!mDicMsgs.ContainsKey(msgType)) mDicMsgs.Add(msgType, null);
        //增加监听
        mDicMsgs[msgType] += handler;
    }
    /// 
    /// 去除对参数handler的监听
    /// 
    /// 消息类型
    /// 被监听方法
    public static void RemoveListener(string msgType, DelMsgHandler handler)
    {
        if (mDicMsgs != null && mDicMsgs.ContainsKey(msgType)) mDicMsgs[msgType] -= handler;
    }

    /// 
    /// 清除所有的监听者
    /// 
    public static void ClearAllListeners()
    {
        if (mDicMsgs != null) mDicMsgs.Clear();
    }

    /// 
    /// 分发消息
    /// 
    /// 消息类型
    /// 分发的内容
    public static void SendMsg(string msgType, Msg msg)
    {
        DelMsgHandler handler;
        if (mDicMsgs != null && mDicMsgs.TryGetValue(msgType, out handler))
        {
            if (handler != null)
                handler(msg);
        }
    }
}

public class Msg
{
    public string Key { get; private set; }

    public object Value { get; private set; }
    public Msg(string key, object value)
    {
        this.Key = key;
        this.Value = value;
    }
}

3、脚本组件的使用方式

​测试脚本1:Test1.cs

using UnityEngine;

public class Test1 : MonoBehaviour
{
    private void Awake()
    {
        //1、通过lambda表达式,如果代码量只有几行,用这个比较方便
        //MsgHandler.AddListener("Test1", (msg) => { });
        //2、通过方法,代码量多时用
        MsgHandler.AddListener("Test1", HandleMsg);
    }
    /// 
    /// 处理消息
    /// 
    /// 消息
    private void HandleMsg(Msg msg)
    {
        switch (msg.Key)
        {
            case "msg key":
                Debug.Log(msg.Value);
                break;
            default:
                Debug.Log("ERROR KEY:" + msg.Key);
                break;
        }
    }

    private void OnDestroy()
    {
        //在脚本被销毁之前,要清除这个监听器
        MsgHandler.RemoveListener("Test1", HandleMsg);
    }
}

测试脚本2:Test2.cs

using UnityEngine;

public class Test2 : MonoBehaviour
{
    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            //发送消息类型为“Test1”的消息
            MsgHandler.SendMsg("Test1", new Msg("msg key", "msg value"));
        }
    } 
}

将这两个脚本分别挂载在两个游戏物体上,运行程序,点击鼠标即可在控制台中输出日志信息:msg value

注意:一定要在OnDestory()中移出监听器,理由是:假如没有销毁,在脚本A在A场景中使用了,在B场景中也使用了, 那么当A场景跳转至B场景时,然后再发消息给脚本A,那么脚本A就会收到2条消息

4、Demo下载

点击下载

你可能感兴趣的:(Unity)