委托是什么&为什么要用委托?
在游戏中,每一帧都会有很多的事件发生,例如地雷爆炸啦、增加金币啦,甚至玩家等级的提升、GameOver都属于某种事件的触发。
很好,我们提到了触发。
那什么是触发呢?从字面意思来看,触发就是触碰到了某个条件,将要发生某件事情。对了!将要发生某件事情!
当我们达到了触发的条件而触发了某个事件之后,这个时候,事件就会被事件的监听者响应,从而去执行自己在这个状态下要做的事。比如GameOver时弹出的结算面板;增加金币时发出‘叮铃’声、屏幕上跳出的金色钱币!
那么,监听者从何而来呢?
这就要引入一个新名词啦–委托!
委托是什么?
我当然可以去百科上赋值粘贴上很多很多的专业名词来绕的你团团转,但良心的谴责让我过意不去,那么就用一句话来总结吧!
委托是种类型。准确的说,是由我们自己定义的类型。
委托是一种类型?这时你肯定会有这样的迷惑:它和之前所说的事件触发有什么关系呢?–当然,继续看下去,你会明白的。
委托是一种类型,是由我们自己定义的类型。而由它定义出来的变量,就是我所说的事件了!
我们来思考一个问题:此时我在A类中定义了一个int a = 10,此时我输出他,他是多少呢?没错,就是10。那么我在它输出之前,在B类中修改了它的值为20呢,最后输出结果是什么?如果你的回答是20,那么恭喜你,答对了。
变量可以通过赋值的操作来修改值,那么为什么方法不可以呢?
没错!委托定义出来的变量,就是一个可以赋值可以调用的方法变量!
而且通过委托变量特有的+=操作,可以将很多方法添加进一个变量中,而此时使用这个变量后,就等于将它身上所有的方法都调用了,从而达到一个调用多个响应的效果!
也就是说,我们可以在player中定义一个委托变量,让所有的敌人都监听这个方法(也就是+=,这么说显得专业),当player的血量掉为0时,调用一下这个方法。与此同时,所有的敌人都知道player死了,也就不会继续进行攻击等操作了。
再举个例子:当你的player吃掉了一个金币,这个时候,会调用身上吃到金币的委托方法。而此时,对它有监听的(UI、Audio等)事物都会去执行相对应的方法。
也许你会有这样的疑问:为什么我非要使用这样的方法呢?明明获取到他们的组件,来修改它们也可以得到相对应效果。
是的,这样写固然没有问题,但为何我们不用一种耦合度更低,更易于维护的方法呢?
现在回到我们第二个问题,为什么要用委托呢?
前面也说过了,委托是由自己定义的一种类型,也就是说只要你能想到的数据结构类型,你都可以定义出来。而这样对我们的好处是可以通过委托来传递参数,达到不仅仅是监听这种效果,而是实现了一种低耦合的消息传递!
举个简单的例子,我们要做一个技能系统,需要实现按键进行技能释放的效果–这好办!在UpDate方法里使用Input.Getkey方法就好了!多么简单而有效!
咳咳,我们的需求这个时候变了,要进行技能更换操作–你会一脸懵逼的:为什么要修改技能位置!
需求和变动往往会在你意想不到的时候来临,所以要随时做好变更的准备,身为程序员更要有这种思想–任何东西都不要写死。一旦写死代码说明代码的扩展性和维护性极差,而一个小小的需求就会让你手忙脚乱。
这里我们选择使用委托的方法来进行技能的绑定!
首先我们定义了一个Q技能的委托,然后在UpDate中使用Input.Getkey……嗯。
当按下Q时,判断这个Q是否为空,不为空就释放掉。
这时,当有需求更改技能位置时,只需要更换一下这个Q对原本技能方法的引用就好了。
同时,使用委托可以实现一个功能:消息中心。
消息中心的核心思路:
在消息中心类中,持有一个字典,键为事件名,值为事件。
类中方法:绑定事件、解绑事件、执行事件。
绑定事件:
传递过来事件名(键)和事件(值),判断键是否存在于字典,如果存在,则进行加等操作,否则就将它添加入字典中。
解绑事件:
传递过来事件名(键)和事件(值),判断键是否存在于字典,如果存在,就进行减等操作,若减等后字典中对应键值为空,则移除该键。
执行事件:
传递过来一个事件名,判断是否存在于字典中,如果存在,则调用对应方法。
例如:敌人生成后向消息中心注册了一个事件:键【GameOver】,值【停止方法】(这里的值是一个委托类型的方法–类型为自己定义的,可以传递一些消息什么的)。一共生成了五个敌人,都去消息中心注册了这个事件。这个时候,Player死掉了,调用了消息中心的执行事件方法,将【GameOver】传递了过去,这时候,所有注册过的敌人,都会执行【停止方法】。
以上,下面是代码实现。
/// <summary>
/// 数据模板类
/// </summary>
public class DataEvg
{
//里面一个list用于存放数据
public ArrayList list = new ArrayList();
public DataEvg( ArrayList list )
{
this.list = list;
}
}
/// <summary>
/// 消息发送类
/// </summary>
public class MsgCenter2 : SingletonClass<MsgCenter2>
{
Dictionary<string, Action<DataEvg>> dic = new Dictionary<string, Action<DataEvg>>();
/// <summary>
/// 注册事件
/// </summary>
/// <param name="actionName">事件名</param>
/// <param name="actionAndData">事件(带着数据)</param>
public void AddAction( string actionName,Action<DataEvg> actionAndData )
{
if (dic.ContainsKey(actionName))
{
dic[actionName] += actionAndData;
}
else
{
dic.Add(actionName, actionAndData);
}
}
/// <summary>
/// 移除事件
/// </summary>
/// <param name="actionName">事件名</param>
/// <param name="actionAndData">事件(带着数据)</param>
public void RemoveAction(string actionName, Action<DataEvg> actionAndData)
{
if (dic.ContainsKey(actionName))
{
dic[actionName] -= actionAndData;
if (dic[actionName] == null)
{
dic.Remove(actionName);
}
}
}
/// <summary>
/// 执行事件
/// </summary>
/// <param name="actionName">事件名</param>
/// <param name="data">穿过来的参数</param>
public void ExeAction(string actionName,DataEvg data)
{
if (dic.ContainsKey(actionName)&& dic[actionName] != null)
{
dic[actionName](data);
}
}
}
好了,今天的讲解就到这里,当然博主还没弄得太明白,所以很多东西讲的很模糊,还请大家体谅,博主会在接下来的时间继续好好学习的!