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

2 篇文章 0 订阅

    在项目中常常会用到观察者模式,消息分发这种功能。最简单的方式可以使用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。 哭

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值