您的位置:首页 > 移动开发 > Unity3D

Unity中实现高效Signal-slot模式--高效消息分发

2016-04-01 10:32 477 查看
在项目中常常会用到观察者模式,消息分发这种功能。最简单的方式可以使用Unity自带的SendMessage来实现,但这种方式通过反射实现效率低下,并且消息接收者必须是GameObject类型,因此在实践中用到更多是的C#的delegate方式,但delegate实现并不能让消息的发送者和接收者解耦合。因此,我们需要一个消息的分发中心类来转发消息。发出消息的对象只需要知道这个消息中心类就行了,不需要关心有没有对象收到这个消息。

因此,我们的类是一个单例

public class SignalMgr	{

public static SignalMgr instance = new SignalMgr();


为了高效地进行消息委托的查找,我们将消息以数组的方式存储起来。

public enum GAME_EVT
{
// game common event
GAME_START = 0,
GAME_END,
HUD_CAMERA_MOVE,
MAX,
}
// define event name here
public class SignalMgr	{

public static SignalMgr instance = new SignalMgr();
List<System.Delegate> _msgMap = null;

public SignalMgr()
{
_msgMap = new List<Delegate>();

for (int i = 0; i < (int)GAME_EVT.MAX; ++i)
{
_msgMap.Add(null);
}
}

public void Clear()
{
_msgMap.Clear();
}
现在该是处理消息参数的时候了,常见的方式是将消息参数写成一个通用的类,将不同的参数转为object对象来传递。在handler中再将object转换为具体参数对象类似。这种方式因为存在装箱效率自然略差,另外如果参数传递较多,还会为不同的参数写一个包装结构体。为了避免这些问题,我采用了泛型传参。

首先定义方法的函数签名:

public delegate void funsig();
public delegate void funsig<T>(T t1);
public delegate void funsig<T1, T2>(T1 t1, T2 t2);
public delegate void funsig<T1, T2, T3>(T1 t1, T2 t2, T3 t3);
public delegate void funsig<T1, T2, T3, T4>(T1 t1, T2 t2, T3 t3, T4 t4);
public delegate void funsig<T1, T2, T3, T4, T5>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5);

如果分发函数带返回值,那么再定义带返回值的签名

public delegate T funsigret<T>();
public delegate T funsigret<T, T1>(T1 t1);
public delegate T funsigret<T, T1, T2>(T1 t1, T2 t2);
public delegate T funsigret<T, T1, T2, T3>(T1 t1, T2 t2, T3 t3);
public delegate T funsigret<T, T1, T2, T3, T4>(T1 t1, T2 t2, T3 t3, T4 t4);
public delegate T funsigret<T, T1, T2, T3, T4, T5>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5);
消息的订阅方法
public void Subscribe(GAME_EVT signal, Delegate listener)
{
if (listener == null)
{
return;
}

Delegate func = _msgMap[(int)signal];
if(func != null)
{
func = Delegate.Combine(func, listener);
}
else
{
func = listener;
}
_msgMap[(int)signal] = func;
}

消息的发出:

public void Raise(GAME_EVT signal)
{
Delegate func = _msgMap[(int)signal];
if(func != null)
{
funsig tmp = (funsig)func;
tmp();
}
}

public void Raise<T1>(GAME_EVT signal, T1 args)
{
Delegate func = _msgMap[(int)signal];
if(func != null)
{
funsig<T1> tmp = (funsig<T1>)func;
tmp(args);
}
}

public void Raise<T1, T2>(GAME_EVT signal, T1 arg1, T2 arg2)
{
Delegate func = _msgMap[(int)signal];
if(func != null)
{
funsig<T1, T2> tmp = (funsig<T1, T2>)func;
tmp(arg1, arg2);
}
}


订阅方调用:注意我们在调用的时候是给出了函数签名,这样在被强转为Delegate类型时函数签名信息才会被保存到Delegate对象中,最后在发消息将Delegate类型强转回来时才能正确地进行类型转换。按理说如果我用泛型多重载几个subscribe函数,listener的签名函数是可以被编译器自动推导的,但我尝试了不行(C++肯定是可以的)。因此很无奈,这里只能自己进行显示的签名转换。

SignalMgr.instance.Subscribe (GAME_EVT.HUD_CAMERA_MOVE, (funsig<float, float>)OnCameraMove);
void OnCameraMove (float deltaPixelX, float deltaPixelY)
{}


发送方调用:幸好这里泛型给力,可以自动推导参数

SignalMgr.instance.Raise(GAME_EVT.HUD_CAMERA_MOVE, deltaPixelX, deltaPixelY);


有了这个基础类,项目中的消息分发就太容易了,而且效率和直接用Delegate几乎没有区别。

下面的全部的源码,供大家参考:

namespace signalModule
{
public delegate void funsig();
public delegate void funsig<T>(T t1);
public delegate void funsig<T1, T2>(T1 t1, T2 t2);
public delegate void funsig<T1, T2, T3>(T1 t1, T2 t2, T3 t3);
public delegate void funsig<T1, T2, T3, T4>(T1 t1, T2 t2, T3 t3, T4 t4);
public delegate void funsig<T1, T2, T3, T4, T5>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5);

public delegate T funsigret<T>();
public delegate T funsigret<T, T1>(T1 t1);
public delegate T funsigret<T, T1, T2>(T1 t1, T2 t2);
public delegate T funsigret<T, T1, T2, T3>(T1 t1, T2 t2, T3 t3);
public delegate T funsigret<T, T1, T2, T3, T4>(T1 t1, T2 t2, T3 t3, T4 t4);
public delegate T funsigret<T, T1, T2, T3, T4, T5>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5);

public enum GAME_EVT
{
// game common event
GAME_START = 0,
GAME_END,
HUD_CAMERA_MOVE,
MAX,
}

// define event name here
public class SignalMgr	{

public static SignalMgr instance = new SignalMgr();
List<System.Delegate> _msgMap = null;

public SignalMgr()
{
_msgMap = new List<Delegate>();

for (int i = 0; i < (int)GAME_EVT.MAX; ++i)
{
_msgMap.Add(null);
}
}

public void Dispose()
{
_msgMap.Clear();
}

public void Subscribe(GAME_EVT signal, Delegate listener)
{
if (listener == null)
{
return;
}

Delegate func = _msgMap[(int)signal];
if(func != null)
{
func = Delegate.Combine(func, listener);
}
else
{
func = listener;
}
_msgMap[(int)signal] = func;
}

public void Unsubscribe(GAME_EVT signal, Delegate listener)
{
if (listener == null)
{
return;
}

Delegate func = _msgMap[(int)signal];
if(func != null)
{
func = Delegate.RemoveAll(func, listener);
}
_msgMap[(int)signal] = func;
}

public void Raise(GAME_EVT signal)
{
Delegate func = _msgMap[(int)signal];
if(func != null)
{
funsig tmp = (funsig)func;
tmp();
}
}

public void Raise<T1>(GAME_EVT signal, T1 args)
{
Delegate func = _msgMap[(int)signal];
if(func != null)
{
funsig<T1> tmp = (funsig<T1>)func;
tmp(args);
}
}

public void Raise<T1, T2>(GAME_EVT signal, T1 arg1, T2 arg2)
{
Delegate func = _msgMap[(int)signal];
if(func != null)
{
funsig<T1, T2> tmp = (funsig<T1, T2>)func;
tmp(arg1, arg2);
}
}

public void Raise<T1, T2, T3>(GAME_EVT signal, T1 arg1, T2 arg2, T3 arg3)
{
Delegate func = _msgMap[(int)signal];
if(func != null)
{
funsig<T1, T2, T3> tmp = (funsig<T1, T2, T3>)func;
tmp(arg1, arg2, arg3);
}
}

public void Raise<T1, T2, T3, T4>(GAME_EVT signal, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
{
Delegate func = _msgMap[(int)signal];
if(func != null)
{
funsig<T1, T2, T3, T4> tmp = (funsig<T1, T2, T3, T4>)func;
tmp(arg1, arg2, arg3, arg4);
}
}

public void Raise<T1, T2, T3, T4, T5>(GAME_EVT signal, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
{
Delegate func = _msgMap[(int)signal];
if(func != null)
{
funsig<T1, T2, T3, T4, T5> tmp = (funsig<T1, T2, T3, T4, T5>)func;
tmp(arg1, arg2, arg3, arg4, arg5);
}
}

public T RaiseRet<T>(GAME_EVT signal) where T : new()
{
Delegate func = _msgMap[(int)signal];
if(func != null)
{
funsigret<T> tmp = (funsigret<T>)func;
return tmp();
}
return new T();
}

public T RaiseRet<T, T1>(GAME_EVT signal, T1 args) where T : new()
{
Delegate func = _msgMap[(int)signal];
if(func != null)
{
funsigret<T, T1> tmp = (funsigret<T, T1>)func;
return tmp(args);
}
return new T();
}

public T RaiseRet<T, T1, T2>(GAME_EVT signal, T1 arg1, T2 arg2) where T : new()
{
Delegate func = _msgMap[(int)signal];
if(func != null)
{
funsigret<T, T1, T2> tmp = (funsigret<T, T1, T2>)func;
return tmp(arg1, arg2);
}
return new T();
}

public T RaiseRet<T, T1, T2, T3>(GAME_EVT signal, T1 arg1, T2 arg2, T3 arg3) where T : new()
{
Delegate func = _msgMap[(int)signal];
if(func != null)
{
funsigret<T, T1, T2, T3> tmp = (funsigret<T, T1, T2, T3>)func;
return tmp(arg1, arg2, arg3);
}
return new T();
}

public T RaiseRet<T, T1, T2, T3, T4>(GAME_EVT signal, T1 arg1, T2 arg2, T3 arg3, T4 arg4) where T : new()
{
Delegate func = _msgMap[(int)signal];
if(func != null)
{
funsigret<T, T1, T2, T3, T4> tmp = (funsigret<T, T1, T2, T3, T4>)func;
return tmp(arg1, arg2, arg3, arg4);
}
return new T();
}

public T RaiseRet<T, T1, T2, T3, T4, T5>(GAME_EVT signal, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) where T : new()
{
Delegate func = _msgMap[(int)signal];
if(func != null)
{
funsigret<T, T1, T2, T3, T4, T5> tmp = (funsigret<T, T1, T2, T3, T4, T5>)func;
return tmp(arg1, arg2, arg3, arg4, arg5);
}
return new T();
}
}
}


看到这里,让我想起了boost的function。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: