生命周期函数返回值为IEnumerator

问题:IEnumerator 在Start()的作用是什么

今天遇到一段代码,如下:

IEnumerator Start()
{
    var delay = new WaitForSeconds(2);
    while (true)
    {
        yield return null;
        if (messages.Count > 0)
        {
            textMeshPro.text = messages.Dequeue();
            spriteRenderer.color = new Color(1, 1, 1, 0);
            textMeshPro.color = new Color(1, 1, 1, 0);
            var T = 0f;
            while (T < 1)
            {
                T += Time.deltaTime;
                spriteRenderer.color = new Color(1, 1, 1, T);
                textMeshPro.color = new Color(1, 1, 1, T);
                yield return null;
            }
            spriteRenderer.color = Color.white;
            textMeshPro.color = Color.white;
            yield return delay;
            while (T > 0)
            {
                T -= Time.deltaTime;
                spriteRenderer.color = new Color(1, 1, 1, T);
                textMeshPro.color = new Color(1, 1, 1, T);
                yield return null;
            }
            spriteRenderer.color = new Color(1, 1, 1, 0);
            textMeshPro.color = new Color(1, 1, 1, 0);
        }
    }

}

对于Start() 函数为例,默认的用法为

才知道还可以定义为协程,但是这样的作用是什么?

注意,以上二者不能同时使用,参考:https://stackoverflow.com/questions/43833405/having-both-ienumerator-start-and-void-start-in-a-single-scriptarrow-up-right

Start() 内部阻塞执行

找到了这个解答(https://www.itread01.com/content/1549800544.htmlarrow-up-right),终于明白了。

简单来说,用IEnumerator 后可以使用yield来阻塞执行,让Start() 阻塞。(后面补充,只是阻塞Start() 内部,不影响其他)

但是上例代码中,Start() 有那么多yield ,Start() 不是只执行一次吗?

yield return null; 表示等到下一帧,于是新问题出现了:Start() 会在多帧中执行吗

Start() 整体异步执行

搜了一下,大部分都说Start() 只会执行一次

Start只执行一次,在Awake方法执行结束后执行,但在Update方法执行前执行, 主要用于程序UI的初始化操作,比如获取游戏对象或者组件

也没找到答案,于是自己试了下。

输出如下:

wait时间改为5s看看

可以看到,Update()已经在执行了,Start() 也能执行。

从上面的测试表现来看,加了IEnumerator 后,Start() 并不会阻塞,反而会在Update() 时也能执行。

这时候再理解最上面的代码,Start() 里的逻辑如下

  1. 如果有消息,把消息取出来。

  2. 用1s的时间逐渐把透明度设为1,也就是把消息显示出来。

  3. 停留2s,再把透明度逐帧变为0,也就是变成透明的。

这时候又有疑问了!

为什么不在Start() 里用StartCoroutine() 启动一个新协程呢?为什么要用IEnumerator Start()......好吧又回到最初的问题了。

处理初始化时的几帧

这个问题貌似找到答案了,参考:https://forum.unity.com/threads/why-change-void-start-to-ienumerator-start.455280/arrow-up-right

Making Start return an IEnumerator automatically makes it a Coroutine. It's useful if you need your initialisation to take several frames.

If you are just using Start to trigger coroutines that run through the objects lifetime, I prefer to use StartCoroutine. Using Start for anything other then object initialisation is misleading, and can make your code harder to maintain.

翻译如下:

使Start返回一个IEnumerator自动使它成为一个协程。 如果你需要你的初始化取几个帧,这是有用的。

如果您只是使用Start来触发在对象生命周期中运行的协程,我更喜欢使用StartCoroutine。 将Start用于对象初始化之外的任何事情都是具有误导性的,并且会使你的代码更难维护。

也就是说,如果Start() 需要处理最开始的几个帧,可以这样用,如果不关心初始几个帧的话,可以在Start() 里使用StartCoroutine() 来启协程。

带着这个理解再看最初的代码,如果不用IEnumerator Start() ,把整个函数内容用 StartCoroutine() 新起一个协程,emmm 貌似也没啥影响。

好吧,关于这个问题的探索,暂时到此为止了,不管有没有找到正确答案,反正最后这个解释算是说服我了!

最后更新于