C#神器 委托 + Unity神器 协程

作为源生的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的作用。

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

/// <summary>
    /// 延时执行
    /// </summary>
    /// <param name="action">执行的委托</param>
    /// <param name="delaySeconds">延时等待的秒数</param>
    public IEnumerator DelayToInvokeDo(Action action, float delaySeconds)
    {
        yield return new WaitForSeconds(delaySeconds);
        action();
    }
    /// <summary>
    /// 使用例子
    /// </summary>
     StartCoroutine(DelayToInvokeDo(delegate() {
                task.SetActive(true);
                task.transform.position = Vector3.zero;
                task.transform.rotation = Quaternion.Euler(Vector3.zero);
                task.doSomethings();
            },1.5f));


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

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

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

/// <summary>
    /// 延时执行
    /// </summary>
    /// <param name="action">执行的委托</param>
    /// <param name="obj">委托的参数</param>
    /// <param name="delaySeconds">延时等待的秒数</param>
    public IEnumerator DelayToInvokeDo(Action<GameObject> action, GameObject obj,float delaySeconds)
    {
        yield return new WaitForSeconds(delaySeconds);
        action(obj);
    }
    /// <summary>
    /// 使用例子
    /// </summary>
     StartCoroutine(DelayToInvokeDo(delegate(GameObject task) {
                task.SetActive(true);
                task.transform.position = Vector3.zero;
                task.transform.rotation = Quaternion.Euler(Vector3.zero);
                task.doSomethings();
            },GameObject.Find("task1"),1.5f));


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

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



原文地址:https://www.cnblogs.com/liang123/p/6325882.html