游戏蛮牛学习群(纯技术交流,不闲聊):539178957
游戏蛮牛 手机端
开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

首页   >   博客   >   xx88

C#神器 委托 + Unity神器 协程 热度 6

个人分类: unity3d | 2018-6-27 11:06
0 个评论 | 阅读 251 | 收藏 | 举报

作为源生的C#程序员,可能已经非常了解委托(delegate)、行动(Action)以及C#的事件了,不过作为一个半道转C#的程序员而言,这些东西可能还是有些陌生的,虽然委托并非是C#独创,亦非是首创,C++的函数指针就完全类似于委托的功能,但很多东西没有委托的话实现起来还是很伤脑筋的。

本文主要介绍委托与unity协程之间组合开发的便利,实质上也是对平常的学习和工作中学到的东西做个记录。

一、委托

定义委托:public delegate void MyDelegate(int _num);

                //定义一个委托MyDelegate,如同定义一个类一样,此时的委托没有经过实例化是无法使用的,而他的实例化必须接收一个返回值和参数都与他等同的函数,此处的委托MyDelegate只能接收返回值为void,参数为一个int的函数


实例化委托:MyDelegate _MyDelegate=new MyDelegate(TestMod);

                //以TestMod函数实例化一个MyDelegate类型的委托_MyDelegate,此处TestMod函数的定义就应如下:

                        public void TestMod(int _num);

                //之后调用_MyDelegate(100)时就完全等同于调用TestMod(100)


二、协程

定义协程:public IEnumerator MyCoroutine(){ };

                //定义一个协程MyCoroutine,不同于定义类或委托,此时的协程是可以直接使用的,Unity的每一个执行周期里都会包含属于协程的那一部分,只要是场景中属于激活状态的物体(active为true),如同update那般,Unity在渲染的每一帧都会去查找该物体上是否存在协程部分的代码,若存在则加入该物体协程的执行周期,协程部分的代码会如同update那样被Unity每帧执行,当然不同于update的是,大部分协程内部不存在延时的话,一次执行之后便会跳出了,而且既然是处于Unity规定的生命周期里的模块,协程如同update一样当他们所在的物体属于未激活的话(active为false),该物体上所有脚本中包含的协程代码都是不会被执行的。


使用协程:StartCoroutine(MyCoroutine());

                //使用StartCoroutine函数将协程MyCoroutine加入此脚本所在物体的协程执行周期内,如果在渲染的下一帧此物体依旧是处于激活状态的话,那么协程MyCoroutine中的代码便会得到Unity执行。



三、委托+协程

其实网上关于这个的例子很多,我在这里只是做个比较系统的归纳。

还记得Unity的Invoke("test",2)吗(延时2秒后执行函数test)?不得不说这是个很大的坑,test函数不能赋予参数不说,还必须得是在当前类里面的方法,而对于延时执行等控制时间轴的操作,这在任何一个项目中肯定都是不可少的,索性有协程,我们完全可以替换掉Invoke的作用。

我们可以自己写一个延时控制器:

[csharp] view plain copy
  1. /// <summary>  
  2.     /// 延时执行  
  3.     /// </summary>  
  4.     /// <param name="action">执行的委托</param>  
  5.     /// <param name="delaySeconds">延时等待的秒数</param>  
  6.     public IEnumerator DelayToInvokeDo(Action action, float delaySeconds)  
  7.     {  
  8.         yield return new WaitForSeconds(delaySeconds);  
  9.         action();  
  10.     }  
  11.     /// <summary>  
  12.     /// 使用例子  
  13.     /// </summary>  
  14.      StartCoroutine(DelayToInvokeDo(delegate() {  
  15.                 task.SetActive(true);  
  16.                 task.transform.position = Vector3.zero;  
  17.                 task.transform.rotation = Quaternion.Euler(Vector3.zero);  
  18.                 task.doSomethings();  
  19.             },1.5f));  


我们可以看到这里的例子是在1.5秒之后执行一个匿名委托,该委托的内容是将游戏物体task激活,并设置其位置与旋转属性,然后可以做更多的事情。

不过好像有些缺陷,task这个变量是哪里来的?Unity能否识别?事实上无论task是本类的全局静态变量还是普通单例变量,甚至只是一个与此部分协程代码处在同一域中的局部变量,加入到协程执行周期以后,短期内他是不会被释放的,也就不用担心Unity在后续执行到task的SetActive的时候会报空引用的错了。

只不过我们确实可以将之完善一下,或许我想更好的控制task。

[csharp] view plain copy
  1. /// <summary>  
  2.     /// 延时执行  
  3.     /// </summary>  
  4.     /// <param name="action">执行的委托</param>  
  5.     /// <param name="obj">委托的参数</param>  
  6.     /// <param name="delaySeconds">延时等待的秒数</param>  
  7.     public IEnumerator DelayToInvokeDo(Action<GameObject> action, GameObject obj,float delaySeconds)  
  8.     {  
  9.         yield return new WaitForSeconds(delaySeconds);  
  10.         action(obj);  
  11.     }  
  12.     /// <summary>  
  13.     /// 使用例子  
  14.     /// </summary>  
  15.      StartCoroutine(DelayToInvokeDo(delegate(GameObject task) {  
  16.                 task.SetActive(true);  
  17.                 task.transform.position = Vector3.zero;  
  18.                 task.transform.rotation = Quaternion.Euler(Vector3.zero);  
  19.                 task.doSomethings();  
  20.             },GameObject.Find("task1"),1.5f));  


1.5秒之后执行一个匿名委托,该委托的内容是将游戏物体task激活,并设置其位置与旋转属性,然后可以做更多的事情,这里的task是我们从场景中获取的name为task1的物体,将之作为参数传入了委托中。

后续你可能需要更多的参数,更多的种类,不过那只需要在此基础上扩展就可以了,最后将协程DelayToInvokeDo放在一个全局脚本内,定义一个该脚本的静态变量,那么就可以在项目的任何位置加入这个协程了。

6 0

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册帐号

个人分类

阅读排行

评论排行

推荐博客

最新博客

返回顶部