該模式大概是這樣的 根據功能劃分不同的模塊,例如數據模塊,音頻模塊,戰鬥模塊,界面UI模塊等,每個模塊有自己單獨的管理器,該管理器實現單例,並由中介者(消息處理器)訪問,而各個模塊之間的互動不在是直接調用,而是通過創建消息體,有消息處理器分發到 接收方,來實現通訊,好處是可以讓多人開發時效率大大提高,也可以讓代碼更加穩定,健康。
消息處理器 即中介者,負責處理各個模塊發來的消息,並進行分發。爲了進行區分模塊,定義了模塊id,根據該id去找到對應模塊的管理器 ,然後由該管理器去處理消息,或者接着分發給下邊的分部。
同時爲了節省性能,考慮到會使用多個消息,而且數量龐大,就接入對象池,這裏是模擬的一個對象池,並沒有接入真正的對象池, 模塊發消息前要先在中介者哪裏拿到消息體。
//mgrID 模塊ID
public enum MgrID
{
None = 0,
GameManager = 1,//遊戲控制器
UIManager = MsgCenter.msgSpan * 1,//UI模塊
AssetsManager = MsgCenter.msgSpan * 2,//資源模塊
AudioManager = MsgCenter.msgSpan * 4,//音頻模塊
ObjectPoolManager = MsgCenter.msgSpan * 5,//對象池模塊
SceneManager = MsgCenter.msgSpan * 6,//場景管理模塊
NetWorkManager = MsgCenter.msgSpan * 7,//網絡管理
GameDataManager = MsgCenter.msgSpan * 8,//遊戲數據管理
FightManager = MsgCenter.msgSpan * 10,//戰鬥管理
}
//消息處理中心
public class MsgCenter : MonoBehaviour
{
private static MsgCenter instance;
public static MsgCenter Instance
{
get
{
return instance;
}
}
public const int msgSpan = 100;// 消息模塊間隔
private Dictionary<string, Queue<MsgBase>> msgpool;//消息池
private List<MsgBase> activePool;//活躍狀態的消息體
void Awake()
{
instance = this;
msgpool = new Dictionary<string, Queue<MsgBase>>();
activePool = new List<MsgBase>();
}
#region 消息獲取
/// <summary>
/// 獲取無數據消息
/// </summary>
/// <param name="msgid"></param>
/// <returns></returns>
public MsgBase GetMsg(ushort msgid)
{
MsgBase msg;
Queue<MsgBase> queue;
if (msgpool.TryGetValue("None", out queue))
{
if (queue.Count > 0)
{
msg = queue.Dequeue();
msg.HasRecyle = false;
}
else
{
//實例化一個新的
msg = new MsgBase();
msg.SetbaseKey("None");
}
}
else
{
queue = new Queue<MsgBase>();
msgpool.Add("None", queue);
msg = new MsgBase();
msg.SetbaseKey("None");
}
msg.msgID = msgid;
activePool.Add(msg);
//Debuger.Log("申請消息: 無參", Color.yellow);
return msg;
}
/// <summary>
/// 獲取單類型消息
/// </summary>
/// <typeparam name="T">傳遞的數據類型</typeparam>
/// <param name="t">傳遞的數據</param>
/// <returns></returns>
public MsgBase GetMsgOne<T>(T t)
{
MsgOne<T> msg;
Queue<MsgBase> queue;
string key = t.GetType().ToString();
//Debuger.Log("消息數據類型:" + key);
if (msgpool.TryGetValue(key, out queue))
{
//Debuger.Log("當前類型的消息剩餘:" + queue.Count);
if (queue.Count > 0)
{
msg = queue.Dequeue() as MsgOne<T>;
msg.HasRecyle = false;
}
else
{
//實例化一個新的
msg = new MsgOne<T>();
}
}
else
{
//新建一個池子
queue = new Queue<MsgBase>();
msgpool.Add(key, queue);
//實例化新的消息
msg = new MsgOne<T>();
}
msg.SetData(t);
activePool.Add(msg);
//Debuger.Log("申請消息: 單參數:"+msg .key, Color.yellow);
return msg;
}
public MsgBase GetMsgOne<T>(List<T> list)
{
MsgOne<T> msg;
Queue<MsgBase> queue;
string key = typeof(T).ToString();
//Debuger.Log("消息數據類型:" + key);
if (msgpool.TryGetValue(key, out queue))
{
//Debuger.Log("當前類型的消息剩餘:" + queue.Count);
if (queue.Count > 0)
{
msg = queue.Dequeue() as MsgOne<T>;
msg.HasRecyle = false;
}
else
{
//實例化一個新的
msg = new MsgOne<T>();
}
}
else
{
//新建一個池子
queue = new Queue<MsgBase>();
msgpool.Add(key, queue);
//實例化新的消息
msg = new MsgOne<T>();
}
msg.SetData(list);
activePool.Add(msg);
// Debuger.Log("申請消息: 多參數:" + msg.key, Color.yellow);
return msg;
}
/// <summary>
/// 獲取多類型消息
/// </summary>
/// <typeparam name="T">類型1</typeparam>
/// <typeparam name="T2">類型2</typeparam>
/// <param name="t">類型1數據</param>
/// <param name="t2">類型2數據</param>
/// <returns></returns>
public MsgBase GetMsgMore<T, T2>(T t, T2 t2)
{
MsgMore<T, T2> msg;
Queue<MsgBase> que;
string key = t.GetType().ToString() + "_" + t2.GetType().ToString();
//Debuger.Log("獲取的消息key:" + key, Color.green);
if (msgpool.TryGetValue(key, out que))
{
//Debuger.Log("當前類型的消息剩餘:" + que.Count);
if (que.Count > 0)//池中還有沒使用的,就拿出來
{
msg = que.Dequeue() as MsgMore<T, T2>;
msg.HasRecyle = false;
}
else
{
msg = new MsgMore<T, T2>();
}
}
else
{
//Debuger.Log(activePool.Count.ToString(), Color.yellow);
que = new Queue<MsgBase>();
msgpool.Add(key, que);
msg = new MsgMore<T, T2>();
}
msg.SetData(t, t2);
activePool.Add(msg);
//Debuger.Log("申請消息: 多參數:" + msg.key, Color.yellow);
return msg;
}
public MsgBase GetMsgMore<T, T2>(List<T> t, List<T2> t2)
{
MsgMore<T, T2> msg;
Queue<MsgBase> que;
string key = t.GetType().ToString() + "_" + t2.GetType().ToString();
//Debuger.Log("獲取的消息key:" + key, Color.green);
if (msgpool.TryGetValue(key, out que))
{
//Debuger.Log("當前類型的消息剩餘:" + que.Count);
if (que.Count > 0)//池中還有沒使用的,就拿出來
{
msg = que.Dequeue() as MsgMore<T, T2>;
msg.HasRecyle = false;
}
else
{
msg = new MsgMore<T, T2>();
}
}
else
{
//Debuger.Log(activePool.Count.ToString(), Color.yellow);
que = new Queue<MsgBase>();
msgpool.Add(key, que);
msg = new MsgMore<T, T2>();
}
msg.SetData(t, t2);
activePool.Add(msg);
//Debuger.Log("申請消息: 多參數:" + msg.key, Color.yellow);
return msg;
}
#endregion
public void Clear()
{
//清理消息池
while (activePool.Count > 0)
{
activePool[0].Recyle();
}
}
//處理消息
public void ProcessMsg(MsgBase msg)
{
switch (msg.manager)
{
case MgrID.None:
break;
case MgrID.GameManager:
GameManager.instance.ProcessMsg(msg);
break;
case MgrID.UIManager:
UIManager.instance.ProcessMsg(msg);
break;
case MgrID.AssetsManager:
AssetsManager.instance.ProcessMsg(msg);
break;
case MgrID.AudioManager:
AudioMgr.instance.ProcessMsg(msg);
break;
case MgrID.ObjectPoolManager:
ObjectPoolMgr.instance.ProcessMsg(msg);
break;
case MgrID.SceneManager:
SceneController.instance.ProcessMsg(msg);
break;
case MgrID.GameDataManager:
GameDataManager.instance.ProcessMsg(msg);
break;
case MgrID.FightManager:
FightController.Instance.ProcessMsg(msg);
break;
default:
break;
}
}
//回收消息
public void Recyle(MsgBase msg)
{
//Debuger.Log("回收消息:id :" + msg.msgID + " key:" + msg.key, Color.yellow);
Queue<MsgBase > msgs;
if (msgpool .TryGetValue (msg .key ,out msgs))
{
msgs.Enqueue(msg);
}else
{
msgs = new Queue<MsgBase>();
msgs.Enqueue(msg);
msgpool.Add(msg .key,msgs);
}
activePool.Remove(msg);
}
}
消息體 存儲本次要發送的數據,這裏做成泛型,支持但數據和多數據。並且支持多類型數據的傳輸。將它抽象是爲了在只需要傳遞消息不需要傳遞數據時剩下空間。直接傳遞基類(包含管理器id,消息id)可咦根據項目實際需求擴展
/// <summary>
/// 消息基類 擴展的消息要繼承自此 不傳遞數據
/// </summary>
public class MsgBase
{
public string key;//存入池中的key
public MgrID manager;//附屬的模塊管理器
public ushort msgID;//具體的msgID,接受者要轉換到自己的模塊ID
public bool AutoRecyle = true;//是否自動回收,如果設置爲false,接收方在該消息使用完後不會回收
public bool HasRecyle=false ;//是否已經回收
public MsgBase()
{
}
public void SetbaseKey(string str)
{
key = str;
}
//消息回收
public virtual void Recyle()
{
if (!AutoRecyle) return;
// 回收對象池
MsgCenter.Instance.Recyle(this);
HasRecyle = true;
}
}
//----------------------------------------使用泛型的好處:相當於一個容器,不需要 知道具體的類型,只作爲存儲和傳遞使用,不用再爲每個類型單獨定義-------------------------------------
/// <summary>
/// 帶單個類型數據
/// </summary>
/// <typeparam name="T"></typeparam>
public class MsgOne<T> : MsgBase
{
public List<T> valuelist;
/// <summary>
/// 置入數據
/// </summary>
/// <param name="t"></param>
public void SetData(T t)
{
HasRecyle = false;
if (valuelist == null)
{
valuelist = new List<T>();
}
valuelist.Add(t);
if (string.IsNullOrEmpty(key))
SetKey(t.GetType().ToString());
}
public void SetData(List <T> list)
{
if (valuelist == null)
{
valuelist = new List<T>();
}
valuelist = list;
HasRecyle = false;
if (string.IsNullOrEmpty(key))
SetKey(typeof(T).ToString());
}
private void SetKey(string str)
{
key = str;
}
public override void Recyle()
{
valuelist.Clear();
//Debuger.Log("key :" + key );
base.Recyle();
}
}
/// <summary>
/// 多個類型的數據
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
public class MsgMore<T1,T2>:MsgBase
{
public List<T1> T1list;
public List <T2 >T2list;
public void SetData(T1 t1,T2 t2)
{
HasRecyle = false;
if (T1list == null)
{
T1list = new List<T1>();
T2list = new List<T2>();
}
T1list.Add(t1);
T2list.Add(t2);
if (string.IsNullOrEmpty(key))
SetKey(t1.GetType().ToString() + "_" + t2.GetType().ToString());
}
public void SetData(List<T1>t1s,List <T2 >t2s)
{
HasRecyle = false;
T1list = t1s;
T2list = t2s;
if (string.IsNullOrEmpty(key))
SetKey(typeof(T1).ToString() + "_" + typeof(T2).ToString());
}
public void SetKey(string str)
{
key = str;
//Debuger.Log("消息key:" + str);
}
public override void Recyle()
{
T1list.Clear();
T2list.Clear();
base.Recyle();
}
}
模塊管理器 負責處理模塊內部所有的消息的分發,和傳遞,內部的部分如果有接受消息和發送的需求都要註冊到管理器內,這樣當管理器接收到對應的消息,才能找到接受者。
//模塊管理器基類
public class MgrBase : MonoBehaviour
{
//消息節點 用於存儲消息
public class MsgNode
{
public MsgNode nextNode;//下一節點
public PartBase part;//當前接受的對象
public MsgNode()
{
}
public MsgNode(PartBase part)
{
this.part = part;
nextNode = null;
}
public MsgNode(PartBase part, MsgNode next)
{
this.part = part;
nextNode = next;
}
}
public MgrID ID;//管理器ID
protected Dictionary<ushort , MsgNode> dic_MsgNode;//該模塊的消息池 id : node
void Awake()
{
dic_MsgNode = new Dictionary<ushort, MsgNode>();
OnAwake();
}
public virtual void OnAwake() { }
//註冊消息
public void RegisterMsg(ushort msgID,PartBase part)
{
if(dic_MsgNode .ContainsKey (msgID))
{
//拿到末尾的
MsgNode node=dic_MsgNode [msgID ];
while(node .nextNode !=null)
{
node = node.nextNode;
}
//存儲
node.nextNode = new MsgNode(part);
}
else
{
dic_MsgNode.Add(msgID, new MsgNode(part));
}
//Debug.Log("zuce:" + msgID);
}
//反註冊消息
public void UnRegisterMsg(ushort msgID,PartBase part)
{
//Debuger.Log("反註冊:L" + part .gameObject .name);
MsgNode cur_Node,lastNode;
if (dic_MsgNode .TryGetValue (msgID,out cur_Node ))//此時拿到的是根節點
{
if (cur_Node.part == part)//移除的是根節點
{
if (cur_Node.nextNode != null)
dic_MsgNode[msgID] = cur_Node.nextNode;//更換頭結點
else
dic_MsgNode.Remove(msgID);//清楚該消息的監聽
}
else
{
lastNode = cur_Node;//要刪除節點的上一個節點
while (cur_Node.part != part)
{
lastNode = cur_Node;//要刪除節點的上一個節點
cur_Node = cur_Node.nextNode;
}
//將當前節點的上一個的下級節點指向當前節點的下級節點
if (cur_Node.nextNode != null)
{
lastNode.nextNode = cur_Node.nextNode;//吧當前節點移除掉
}
else
{
//說明當前節點爲尾節點
lastNode.nextNode = null;
}
}
}
else
{
Debuger.Log("未註冊該消息:" + msgID, Color.red);
}
}
//清空
public void Clear()
{
dic_MsgNode.Clear();
dic_MsgNode = new Dictionary<ushort, MsgNode>();
}
/// <summary>
/// 發送消息
/// </summary>
public virtual void Send(MsgBase msg)
{
if (msg .HasRecyle)
{
Debuger.Log("當前消息已經被回收,請確認發送的消息!",Color .red);
return;
}
MsgCenter.Instance.ProcessMsg(msg);
}
/// <summary>
///
/// </summary>
public virtual void ProcessMsg(MsgBase msg)
{
}
/// <summary>
/// 分發模塊內部消息
/// </summary>
public void Notify(MsgBase msg)
{
ushort id = msg.msgID;
if (dic_MsgNode.ContainsKey(id))
{
MsgNode node = dic_MsgNode[id];
node.part.ProcessMsg(msg);
while (node.nextNode != null&& !msg .HasRecyle )
{
node = node.nextNode;
node.part.ProcessMsg(msg);
}
}
else
{
Debuger.LogError("該消息尚未註冊!!" + id);
}
}
}
模塊分部基類 只有繼承或者掛載了該類的才能進行數據接受分發,當然,要在初始化的時候註冊自己到所在模塊管理器內,然後將要監聽的消息id進行實例化並配置
/// <summary>
/// 所有涉及到消息接受處理的基類
/// </summary>
public class PartBase : MonoBehaviour
{
public MgrBase manager;//該模塊管理器
public ushort[] msgIDs;
private bool hasrecle;
/// <summary>
/// 發送消息
/// </summary>
public virtual void Send(MsgBase msg)
{
if (msg.HasRecyle)
{
Debuger.Log("當前消息已經被回收,請確認發送的消息!", Color.red);
return;
}
if (manager == null)
{
Debuger.Log("請先指定模塊管理器!!"+gameObject .name, Color.red);
return;
}
if (manager.ID == msg.manager)
manager.ProcessMsg(msg);//內部通信,管理器直接處理
else
{
manager.Send(msg);//外部消息由管理器統一傳遞
}
}
/// <summary>
/// 處理消息
/// </summary>
public virtual void ProcessMsg(MsgBase msg)
{
}
//註冊消息
public void RegisteSelf(MgrBase mgr)
{
manager = mgr;
if (msgIDs ==null ||msgIDs .Length == 0)
{
Debuger .LogWarning("注意:該部分未註冊任何消息,請確認!"+gameObject .name);
}else
{
if(manager ==null)
{
Debuger.LogError("請先指定模塊管理器!!");
return;
}
for (int i = 0; i < msgIDs .Length ; i++)
{
manager.RegisterMsg(msgIDs[i], this);
}
}
}
//反註冊消息
public void UnRegistSelf()
{
if (hasrecle) return;
hasrecle = true;
if (msgIDs == null || msgIDs.Length == 0)
{
Debuger.LogWarning("注意:該部分未註冊任何消息,請確認!" + gameObject.name);
}
else
{
if (manager == null)
{
Debuger.LogError("請先指定模塊管理器!!"+gameObject .name);
return;
}
for (int i = 0; i < msgIDs.Length; i++)
{
manager.UnRegisterMsg(msgIDs[i], this);
//Debuger.Log("反註冊消息:"+msgIDs [i],Color .red);
}
}
}
void OnDestroy()
{
UnRegistSelf();
}
}
舉個例子:
MsgOne<string> sendmsg = MsgCenter.Instance.GetMsgOne<string>(Showplayer.freshReadyCards[index].info.Name) as MsgOne<string>;
sendmsg.manager = MgrID.AudioManager;
sendmsg.msgID = (ushort)AudioMsgID.PlayHeroSay;
Send(sendmsg);