Unity3D基于事件机制的消息系统

又到了周末时间,今天没有去公司赚加班费,在家里好好休息一下?不存在的,这辈子不写代码是不存在的!哭

今天我们来讲讲消息系统。

很多刚入行的小白同学在处理类与类之间的关系时,总是比较简单除暴的处理,直接把那个类引用到这个类,把这个类引用到那个类,最后造成很多类相互引用,形成一个复杂的蜘蛛网式的引用关系,这就是代码的耦合。那这个又有什么关系呢?那么我们就通俗的讲一下这个问题。

打个比方,同学们的期末成绩出来了,然后学校派一个人到全校的学生家里一个个通知成绩,第二天这个人辞职了哈哈!那么学校会怎么做呢?在远古时代,学校一般都是会在学校的公示栏上公布所有学生的期末成绩,而在如今的互联网时代,学校一般都是把所有学生的期末成绩发布到自己系统内,想要知道成绩的同学自己去查看,这样就不会有人辞职了哈哈。学校的系统不认识是哪个学生,也不关心哪个学生要不要看自己的成绩,反正我发布出去了,有兴趣要看的同学可以自己去查看,我们之间没有任何的绯闻,你成绩好不好和我没关系。

这个过程就叫解耦,类与类之间不再有耦合关系,没有小三,也没有小四,家和万事兴。

所以我们引入了事件机制,事件机制其实是一种叫做观察者模式的设计模式,事件的本质是一种方法的委托(Delegate),把回调方法委托到事件管理器,当条件达到时,通过事件key来告诉事件管理器可以执行那些委托的方法。

好了,接下来XM就为大家讲解一套完整的消息系统。

1.首先我们先来定义一下消息结构的接口IMessage。

// **********************************************************************
// Copyright (C) XM
// Author: 吴肖牧
// Date: 2018-04-13
// Desc: 
// **********************************************************************

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public interface IMessage
{
    /// <summary>
    /// 事件类型,Key
    /// </summary>
    int Type { get; set; }

    /// <summary>
    /// 发送者
    /// </summary>
    System.Object Sender { get; set; }

    /// <summary>
    /// 参数
    /// </summary>
    System.Object[] Params { get; set; }

    /// <summary>
    /// 转字符串
    /// </summary>
    /// <returns></returns>
    string ToString();
}

为什么我会用int类型而不是用string类型作为消息的key呢?大家可以自己思考一下。

2.接口IMessage的实现Message。

// **********************************************************************
// Copyright (C) XM
// Author: 吴肖牧
// Date: 2018-04-13
// Desc: 
// **********************************************************************

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Message : IMessage {
    public int Type { get; set; }

    public System.Object[] Params { get; set; }

    public System.Object Sender { get; set; }

    public override string ToString()
    {
        string arg = null;
        if (Params != null)
        {
            for (int i = 0; i < Params.Length; i++)
            {
                if ((Params.Length > 1 && Params.Length - 1 == i) || Params.Length == 1)
                {
                    arg += Params[i];
                }
                else
                {
                    arg += Params[i] + " , ";
                }
            }
        }

        return Type + " [ " + ((Sender == null) ? "null" : Sender.ToString()) + " ] " + " [ " + ((arg == null) ? "null" : arg.ToString()) + " ] ";
    }

    public Message Clone()
    {
        return new Message(Type, Params, Sender);
    }

    public Message(int type)
    {
        Type = type;
    }

    public Message(int type, params System.Object[] param)
    {
        Type = type;
        Params = param;
    }

    public Message(int type, System.Object sender, params System.Object[] param)
    {
        Type = type;
        Params = param;
        Sender = sender;
    }
}

3.接下来我们定义一下消息的类型,这里我把消息类型分成了常用消息,战斗消息,协议消息3大类,而不是都写在一个类型里面,结构清晰明了。

// **********************************************************************
// Copyright (C) XM
// Author: 吴肖牧
// Date: 2018-04-13
// Desc: 消息的类型
// **********************************************************************

/// <summary>
/// 消息的类型  
/// </summary>
public enum MessageType
{
    /// <summary>
    /// 启动
    /// </summary>
    START_UP = 1000,
    /// <summary>
    /// 解压
    /// </summary>
    UNPACK,
    /// <summary>
    /// 更新
    /// </summary>
    UPDATE,
    /// <summary>
    /// 更新完成
    /// </summary>
    UPDATE_COMPLETE,
}


/// <summary>
/// 战斗的类型
/// </summary>
public enum BattleEvent
{
    /// <summary>
    /// 攻击
    /// </summary>
    Attack = 10000,
}

/// <summary>
/// 协议的类型
/// </summary>
public enum ProtocolEvent
{

}

4.然后我们定义一下消息派发者的接口IDispatcher。

// **********************************************************************
// Copyright (C) XM
// Author: 吴肖牧
// Date: 2018-04-13
// Desc: 
// **********************************************************************

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public interface IDispatcher
{
    void AddListener(int type, EventListenerDelegate listener);

    void RemoveListener(int type, EventListenerDelegate listener);

    void SendMessage(Message evt);

    void SendMessage(int type, params System.Object[] param);

    void Clear();
}

5.接口IDispatcher的实现Dispatcher。

// **********************************************************************
// Copyright (C) XM
// Author: 吴肖牧
// Date: 2018-04-13
// Desc: 
// **********************************************************************

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public delegate void EventListenerDelegate(Message evt);

public class Dispatcher : Singleton<Dispatcher>, IDispatcher
{
    
    private Dictionary<int, EventListenerDelegate> events = new Dictionary<int, EventListenerDelegate>();

    public void AddListener(int type, EventListenerDelegate listener)
    {
        if (listener == null)
        {
            XMDebug.LogError("AddListener: listener不能为空");
            return;
        }

        EventListenerDelegate myListener = null;
        events.TryGetValue(type, out myListener);
        events[type] = (EventListenerDelegate)Delegate.Combine(myListener, listener);
    }


    public void RemoveListener(int type, EventListenerDelegate listener)
    {
        if (listener == null)
        {
            XMDebug.LogError("RemoveListener: listener不能为空");
            return;
        }

        events[type] = (EventListenerDelegate)Delegate.Remove(events[type], listener);
    }

    public void Clear()
    {
        events.Clear();
    }

    public void SendMessage(Message evt)
    {
        EventListenerDelegate listenerDelegate;
        if (events.TryGetValue(evt.Type, out listenerDelegate))
        {
            try
            {
                if (listenerDelegate != null)
                {
                    listenerDelegate(evt);
                }
            }
            catch (System.Exception e)
            {
                XMDebug.LogError("SendMessage:", evt.Type.ToString(), e.Message, e.StackTrace, e);
            }
        }
    }

    public void SendMessage(int type, params System.Object[] param)
    {
        EventListenerDelegate listenerDelegate;
        if (events.TryGetValue(type, out listenerDelegate))
        {
            Message evt = new Message(type, param);
            try
            {
                if (listenerDelegate != null)
                {
                    listenerDelegate(evt);
                }
            }
            catch (System.Exception e)
            {
                XMDebug.LogError("SendMessage:", evt.Type.ToString(), e.Message, e.StackTrace, e);
            }
        }
    }


    public void AddListener(MessageType type, EventListenerDelegate listener)
    {
        AddListener((int)type, listener);
    }

    public void AddListener(BattleEvent type, EventListenerDelegate listener)
    {
        AddListener((int)type, listener);
    }

    public void AddListener(ProtocolEvent type, EventListenerDelegate listener)
    {
        AddListener((int)type, listener);
    }

    public void RemoveListener(MessageType type, EventListenerDelegate listener)
    {
        RemoveListener((int)type, listener);
    }

    public void RemoveListener(BattleEvent type, EventListenerDelegate listener)
    {
        RemoveListener((int)type, listener);
    }

    public void RemoveListener(ProtocolEvent type, EventListenerDelegate listener)
    {
        RemoveListener((int)type, listener);
    }

    public void SendMessage(MessageType type, params System.Object[] param)
    {
        SendMessage((int)type, param);
    }

    public void SendMessage(BattleEvent type, params System.Object[] param)
    {
        SendMessage((int)type, param);
    }

    public void SendMessage(ProtocolEvent type, params System.Object[] param)
    {
        SendMessage((int)type, param);
    }
}

我为每个实现接口的方法又重新封装了3个重载方法,这里主要是为了区分消息的类型,方便更好的维护我们的框架,希望同学们都能养成这样的编写代码习惯。

最后,为了不要再让有人因为跑腿辞职,我们还是用好消息系统,不然他们就要跑去送外卖了偷笑

©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值