Unity 协程(Coroutine)如何自定义类型控制迭代器

最近遇到个问题,是在阅读别人代码的时候产生的。简单来说就是如何自定义一个类型可以让Unity协程识别并且阻塞。这个问题一开始不知道怎么去查,就直接查协程,结果查一大堆告诉我yield return null,yield return new waitForSecond()怎么怎么用。这个对新手确实很重要,需要总结出来,但是不是我想要的答案。

后来历经千辛万苦。。。。好吧也没有千辛万苦,查了一下午。终于找到了相关文章。我根据自己在项目中看到的用法,和自己的亲身实验,在众多文章中挑选了一篇我觉得符合我理解的:https://blog.csdn.net/lrfleroy/article/details/89219965。是一个系列文章,感兴趣的可以看看。

我就总结一下大概原理和用法。

1.原理

Unity的协程是借助迭代器的原理,在StartCoroutine(IEnumerator)传入一个迭代器就可以开启一个协程。而迭代器主要是依靠bool MoveNext()和object current,来确定当前执行的对象和是否可以继续执行下去,下面是IEnumerator接口代码:

namespace System.Collections
{
    public interface IEnumerator
    {
        object Current { get; }

        bool MoveNext();
        void Reset();
    }
}

       关于迭代器内部的实现原理,可以看这篇文章:https://www.jianshu.com/p/ddd023c1a886

       其实Unity利用传入的协程函数中的 yield return 的这些返回值做了特殊处理来实现阻塞迭代器执行的目的。

        首先我们调用StartCoroutine传入的函数本身就是一个迭代器,配合yield关键字这个语法糖(链接:https://www.cnblogs.com/blueberryzzz/p/8678700.html)会让我们每一次执行这个迭代器时获得到它return的一个个元素(其实内部控制了一个指针和list,当调用movenext的时候,指针往前移动,判断下面是否还有元素可访问,如果可以则moveNext为true,返回当前元素Current给外部,reset则就把指针位置重置)。

       而协程函数这个迭代器中返回的又是一个IEnumerator的新的迭代器,Coroutine类就是对这个返回值做了特殊处理,当访问到这个返回值,判断其中的MoveNext是否为false(false就意味着这个迭代器没有元素了,不可继续执行了),然后来控制是否阻塞迭代器的执行。  

       举个例子,我们写一个new waitforSecond(),在new对象的时候记住当前时间,然后内部不停访问这个movenext()方法实时判断是否超过5秒,不就能够控制它在5秒后再执行了吗。

2.用法

用法非常简单,只需要继承IEnumerator接口,控制好movenext方法就好。一个等待固定时间的例子:

public class BaseCoroutineOperation : IEnumerator
{
    float startTime;
    float waitTime;

    public BaseCoroutineOperation(float waitTime)
    {
        this.waitTime = waitTime;
        startTime = Time.time;
    }

    public object Current => null;

    public bool MoveNext()
    {
        return !isDone();
    }

    public void Reset()
    {

    }

    public bool isDone()
    {
        return Time.time - startTime > waitTime;
    }
}

public class CoroutineTestDemo : MonoBehaviour
{
    void Start()
    {
        //Coroutine coroutine = StartCoroutine(CouroutineTest());
        //StopCoroutine(coroutine);
        Coroutine coroutine = StartCoroutine("CouroutineTest");
        StopCoroutine("CouroutineTest");
        Debug.LogError(444);
    }

    IEnumerator CouroutineTest()
    {
        Debug.Log(111);
        yield return new BaseCoroutineOperation(2);
        Debug.Log(222);
        yield break;
        Debug.Log(333);
    }
}

以上例子我已经成功测试过。

另外

发现一个细节,看上面例子,当使用stopCoroutine(IEnumerator inumerator)的时候,并不能如期待的那样终止协程,如果上面程序执行,应该不会等待2秒就会终止掉才对,但是却还是2秒后把222打印了出来。但是当我使用stopCoroutine(string methodName),同时也使用startCoroutine(string methodName)的时候,却成功终止了。

后来找到篇资料:https://blog.csdn.net/as467884505/article/details/85069048

发现如果使用startCoroutine(IEnumerator inumerator)时想要终止协程,可以将startCoroutine返回的Coroutine对象传入stopCoroutine(Coroutine coroutine)进行终止。

也可以参考下面文章:

https://blog.csdn.net/qq_31967569/article/details/83305415

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值