近期用到了網絡通信的方法,雖然unity可以用協程來實作異步操作,不過坑爹的隊友不會用,他用的是傳統的開線程的方法,這樣就會出現線程安全的問題,然後現有的消息通信機制無法滿足需求了,就得改了。還好我機智的看過Cocos2dx中消息機制的實作原理,順手改了一下,下面貼源碼:(源碼後有解釋)
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/**
* 消息派發類
*/
namespace Assets.TGUI.UIScript
{
public class CDispatcher
{
//單例
public static readonly CDispatcher Instance = new CDispatcher();
//消息委托函數
public delegate void noticeDelegate(object[] data);
/// <summary>
/// 消息Key
/// </summary>
public struct Key
{
public string name;
public object target;
public Key(string _name, object _target)
{
name = _name;
target = _target;
}
}
//存儲消息清單
private Dictionary<Key, noticeDelegate> m_noticeDict;
private List<KeyValuePair<Key, object[]>> m_noticeRequest;
private List<KeyValuePair<Key, object[]>> m_tempRequest;
private CDispatcher()
{
m_noticeDict = new Dictionary<Key, noticeDelegate>();
m_noticeRequest = new List<KeyValuePair<Key, object[]>>();
m_tempRequest = new List<KeyValuePair<Key, object[]>>();
}
/// <summary>
/// 循環周遊 執行消息請求
/// </summary>
public IEnumerator Dispatcher()
{
Debug.Log("MSG: Start Dispatcher");
do
{
if (m_tempRequest.Count != 0)
{
lock (m_tempRequest)
{
foreach (var item in m_tempRequest)
{
m_noticeRequest.Add(new KeyValuePair<Key, object[]>(item.Key, item.Value));
}
m_tempRequest.Clear();
}
foreach (var item in m_noticeRequest)
{
if (m_noticeDict.ContainsKey(item.Key))
{
m_noticeDict[item.Key](item.Value);
}
}
m_noticeRequest.Clear();
}
yield return new WaitForFixedUpdate();
} while (true);
}
/**
* 發送消息
* @param notifyName 消息類型
* @param data 攜帶參數
*/
public void sendNotification(string noticeName, object[] data = null)
{
sendNotification(noticeName, null, data);
}
/// <summary>
/// 發送消息
/// </summary>
/// <param name="noticeName"></param>
/// <param name="target"></param>
/// <param name="data"></param>
public void sendNotification(string noticeName, object target, object[] data)
{
m_tempRequest.Add(new KeyValuePair<Key, object[]>(new Key(noticeName, target), data));
}
/// <summary>
/// 發送消息 附帶的資料是發送者本身的對象
/// </summary>
/// <param name="noticeName"></param>
/// <param name="target"></param>
/// <param name="source"></param>
public void sendNotification(string noticeName, object target, object source)
{
sendNotification(noticeName, target, new object[] { source });
}
/// <summary>
/// 擷取到源類型變量
/// (與sendNotification(string noticeName,
/// object target, object source)對應)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data"></param>
/// <returns></returns>
public T GetSourceObject<T>(object[] data)
{
return (T)data[0];
}
public void addObserver(string noticeName, noticeDelegate notice)
{
addObserver(noticeName, null, notice);
}
public void addObserver(string noticeName, object target, noticeDelegate notice)
{
Key key = new Key(noticeName, target);
if (!m_noticeDict.ContainsKey(key))
{
m_noticeDict.Add(key, notice);
}
}
public void RemoveObserver(string noticeName)
{
RemoveObserver(noticeName, null);
}
public void RemoveObserver(string noticeName, object target)
{
Key key = new Key(noticeName, target);
if (!m_noticeDict.ContainsKey(key))
{
m_noticeDict.Remove(key);
}
}
}
}
方法稍微有點略多,但是值得關注的方法就是下面幾個
IEnumerator Dispatcher()
public void sendNotification(...)
public void addObserver(...)
public void RemoveObserver(...)
首先來說第一個,第一個是消息循環周遊的方法,需要在遊戲主對象中的start方法中使用協程開啟消息循環周遊,寫法如下:
//開啟消息循環
StartCoroutine(CDispatcher.Instance.Dispatcher());
然後和Cocos2dx一樣,添加觀察者
CDispatcher.Instance.addObserver("消息名字", [目标對象], 方法);
然後發送消息
CDispatcher.Instance.sendNotification("消息名字",[目标對象],[附加資料]);
下面是一個完整的使用例子:
protected void Start()
{
//開啟消息循環
StartCoroutine(CDispatcher.Instance.Dispatcher());
CDispatcher.Instance.addObserver("我要睡覺", x => Debug.Log("睡覺"));
CDispatcher.Instance.sendNotification("我要睡覺");
}
最後當然輸出“睡覺”咯