近期用到了网络通信的方法,虽然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("我要睡觉");
}
最后当然输出“睡觉”咯