写在前面:
写本博客的初心是为了更好的总结学习游戏框架遇到的知识点,难免会有理解错误的地方,望大神批评指正。
本框架使用的消息分发机制来实现的。重要组成部分如下:
- 消息处理中心MsgCenter
消息处理中心是整个模块的中转站,模块管理者Manager如果想与其它模块通讯的话,必须通过此中转站,中转站通过消息ID找到对应模块,然后交付给此模块内部处理。
提供的功能:注册消息,销毁消息,分发广播消息,更新消息 - 消息体Msg
消息是每个模块传递数据的载体,其必须包含唯一的消息ID,设计架构时根据需要划分每个模块的ID范围,一般用枚举表示范围。 - 模块管理者Manager
模块的管理者Manager统一继承至一个模板类,节省代码,方便管理。
模块管理者调用消息管理中心MsgCenter提供的方法,注册消息,发送消息,接收消息 ,销毁消息。
如果本模块需要与其它模块通信,在注册消息时也需要注册其它模块,这样通过委托事件可以得到其它模块的消息。
消息处理中心MsgCenter
using System;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 消息回调函数
/// </summary>
/// <param name="msg"></param>
public delegate void MsgCallbackd(Msg msg);
public class MsgCenter : MonoBehaviour
{
/*
* 实现功能:
* 1.注册注销消息:消息集合,记录待处理事件,注册消息,销毁消息,广播通知,刷新
* 2.发送消息到对应模块
*/
private static MsgCenter _instance;
/// <summary>
/// 单例
/// </summary>
public static MsgCenter Instance
{
get
{
if (_instance == null)
{
GameObject obj = new GameObject("MsgCenter");
_instance = obj.AddComponent<MsgCenter>();
DontDestroyOnLoad(obj);
}
return _instance;
}
}
/// <summary>
/// 消息集合
/// </summary>
private Dictionary<int, MsgCallbackd> MsgDic = new Dictionary<int, MsgCallbackd>();
/// <summary>
/// 待处理消息集合
/// </summary>
Queue<Action> todoCallBack = new Queue<Action>();
#region 注册消息
/// <summary>
/// 注册多条消息
/// </summary>
/// <param name="callback"></param>
/// <param name="msgIds"></param>
/// <returns></returns>
public bool RegistMsgs(MsgCallbackd callback, int[] msgIds)
{
if (callback == null)
{
Debug.LogWarning("注册消息失败");
return false;
}
for (int i = 0; i < msgIds.Length; i++)
{
RegistMsg(callback, msgIds[i]);
}
Debug.Log("注册消息成功");
return true;
}
/// <summary>
/// 注册一条消息
/// </summary>
/// <param name="callback"></param>
/// <param name="i"></param>
public bool RegistMsg(MsgCallbackd callback, int msgId)
{
//通过操作消息集合MsgDic建立消息ID和委托建立联系
/*
* 1.没有委托内容返回
* 2.集合不存在,添加内容到集合
* 3.集合存在,添加委托事件给消息
*/
if (callback == null)
return false;
if (MsgDic.ContainsKey(msgId))
{
MsgDic[msgId] += callback;
}
else
{
MsgDic.Add(msgId, callback);
}
return true;
}
#endregion
#region 注销消息
/// <summary>
/// 注销多条消息
/// </summary>
/// <param name="msgIds"></param>
/// <returns></returns>
public bool UnRegistMsgs(MsgCallbackd callbackd, int[] msgIds)
{
if (callbackd == null)
return false;
for (int i = 0; i < msgIds.Length; i++)
{
UnRegistMsg(callbackd, msgIds[i]);
}
return true;
}
/// <summary>
/// 注销一条消息
/// </summary>
public bool UnRegistMsg(MsgCallbackd callbackd, int msgId)
{
if (!MsgDic.ContainsKey(msgId) || callbackd == null)
{
return false;
}
else
{
//注意一条id对应多条委托情况
MsgDic[msgId] -= callbackd;
if (MsgDic[msgId] == null)
{
MsgDic.Remove(msgId);
}
return true;
}
}
#endregion
#region 广播分发消息
/// <summary>
/// 广播分发消息
/// </summary>
/// <param name="msg">传入的消息内容</param>
/// <returns></returns>
public bool PostMsg(Msg msg = null)
{
if (msg.msgId > 0 && MsgDic.ContainsKey(msg.msgId) && MsgDic[msg.msgId] != null)
{
//加入队列
lock (todoCallBack)
{
//通过msgId找到对应的方法,加入队列
todoCallBack.Enqueue(() => MsgDic[msg.msgId](msg));
}
return true;
}
return false;
}
#endregion
/// <summary>
/// 刷新待处理消息
/// </summary>
private void Update()
{
if (todoCallBack.Count == 0)
return;
lock (todoCallBack)
{
while (todoCallBack.Count > 0)
{
Action a = todoCallBack.Dequeue();
a.Invoke();
}
todoCallBack.Clear();
}
}
}
消息体
using System.Collections;
public class Msg
{
/// <summary>
/// 消息ID
/// </summary>
public int msgId { get; protected set; }
/// <summary>
/// 发送的内容
/// </summary>
public object sender { get; protected set; }
}
public class MsgSetting
{
public const int IdSpan = 100;
}
public enum MgrId
{
//分模块划分消息ID,定义id起点长度,每个模块单独管理自己id
//0~99
UIMgr=0,
//100~199
ResourceMgr=MsgSetting.IdSpan*2,
//200~299
AudioMgr=MsgSetting.IdSpan*3,
}
模块管理者
首先是管理者模板
using UnityEngine;
/// <summary>
/// 模块管理者
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class MgrSingle<T> : MonoBehaviour where T : MonoBehaviour
{
//MgrSingle<T>告诉子类引用的形式,其中T相当于模板的类型,用where指定
public static T Instance { get; protected set; }
protected void Awake()
{
//if (Instance != null)
// DestroyImmediate(Instance.gameObject);
Instance = this as T;
OnAwake();
}
protected abstract void OnAwake();
}
下面演示不同模块的通讯过程,A模块(ResourceMgr)向B模块(AudioMgr)发送消息时,如果B模块想接收A模块的消息,必须向MsgCenter注册A模块的消息。
A模块:
using UnityEngine;
using System.Collections;
using System;
public class ResourcesMgrDemo : MgrSingle<MonoBehaviour>
{
protected override void OnAwake()
{
MsgCenter.Instance.RegistMsg(CallBack, (int)DemoMsgId.ResourceId);
}
private void OnDestroy()
{
MsgCenter.Instance.UnRegistMsg(CallBack, (int)DemoMsgId.ResourceId);
}
private void CallBack(Msg msg)
{
if (msg == null || msg.msgId < 0)
return;
else
{
switch(msg.msgId)
{
case (int)DemoMsgId.AudioId:
break;
case (int)DemoMsgId.ResourceId:
break;
}
}
}
// Use this for initialization
void Start()
{
//测试Resource模块向Audio模块发送消息
Debug.Log("ddd");
DemoMsg msg = new DemoMsg((int)MgrId.AudioMgr, "admin1", 21, this);
MsgCenter.Instance.PostMsg(msg);
}
// Update is called once per frame
void Update()
{
}
}
B模块:
using UnityEngine;
using System.Collections;
public class AudioMgrDemo : MgrSingle<MonoBehaviour>
{
protected override void OnAwake()
{
MsgCenter.Instance.RegistMsg(CallBack, (int)DemoMsgId.AudioId);
}
private void OnDestroy()
{
MsgCenter.Instance.UnRegistMsg(CallBack, (int)DemoMsgId.AudioId);
}
private void CallBack(Msg msg)
{
if (msg == null || msg.msgId < 0)
return;
else
{
switch (msg.msgId)
{
case (int)DemoMsgId.AudioId:
DemoMsg audioMsg = msg as DemoMsg;
Debug.LogWarning("收到其它模块发送的消息");
Debug.LogFormat("收到AudioId,id:{0},sender:{1},name:{2},age:{3}", audioMsg.msgId, audioMsg.sender, audioMsg.m_Name, audioMsg.m_Age);
break;
case (int)DemoMsgId.ResourceId:
break;
}
}
}
}
测试过程
创建新的游戏物体并添加代码如下:
PS:这样做确实有不妥的地方,因为第一次使用框架,想急切感受框架的魅力,先这样操作,后面的文章我会继续升级。
在AudioMgr模块确实收到了消息,下面验证消息来源是不是ResourceMgr模块的发送的。
恭喜模块通信成功!
参考文章:http://gad.qq.com/article/detail/287237