Unity3D之协程

  Unity3D提供了一个工具叫做“协程”,所谓协程就是使用StartCoroutine()里面添加一个方法来调用该方法。对这个被调用的方法有如下规定:返回值必须是IEnumerator类型。那么为什么要使用协程呢?通常这是为了应付某一类需要,比如想要延时执行某一段代码,或者使用www进行一些请求和加载等阻塞操作。

  协程与多线程没有关系。协程每一帧都执行,时间段位于LateUpdate之后。所以说它只不过是在主线程里面每帧执行的一个函数而已。协程使用的原理类似于foreach循环,都是使用迭代器来实现,因此它也包括IEnumerator接口的Current属性和MoveNext()方法。这两个东东都是迭代器比较重要的内容。

  MSDN上对Current的解释为:Gets the current element in the collection,对MoveNext()的解释为:Advances the enumerator to the next element fo collection。从这两个解释可以看出来,Current返回的是集合当前的元素,MoveNext()推动循环向下一个元素。MoveNext()的返回值是bool,当返回true时,就向下走一级,返回false,则停止。假如循环到最后一个元素,肯定返回false。

  对于协程来说,当进行至yield return语句时,检查yield return后面的条件是否满足,如果满足,则执行后面的代码。如果不满足,则记录下该位置,下一帧继续检查,直到满足为止。这里体现了协程的主要作用了,假如你用www下载一个数据,你肯定想知道什么时候下载完。只有下载完了yield return www的下一行才会得到执行。

  下边看一个测试类,此类来自网络,通过一个协程内的循环来交替执行另外两个协程。注意,这些协程都是在主线程里面,可以随意访问Unity3D的对象和组件。

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using System.Collections;

[RequireComponent(typeof(GUIText))]
public class Hijack : MonoBehaviour {
    
    //This will hold the counting up coroutine
    IEnumerator _countUp;
    //This will hold the counting down coroutine
    IEnumerator _countDown;
    //This is the coroutine we are currently
    //hijacking
    IEnumerator _current;
    
    //A value that will be updated by the coroutine
    //that is currently running
    int value = 0;
    
    void Start()
    {
        //Create our count up coroutine
        _countUp = CountUp();
        //Create our count down coroutine
        _countDown = CountDown();
        //Start our own coroutine for the hijack
        StartCoroutine(DoHijack());
    }
    
    void Update()
    {
        //Show the current value on the screen
        guiText.text = value.ToString();
    }
    
    void OnGUI()
    {
        //Switch between the different functions
        if(GUILayout.Button("Switch functions"))
        {
            if(_current == _countUp)
                _current = _countDown;
            else
                _current = _countUp;
        }
    }
    
    IEnumerator DoHijack()
    {
        while(true)
        {
            //Check if we have a current coroutine and MoveNext on it if we do
            if(_current != null && _current.MoveNext())
            {
                //Return whatever the coroutine yielded, so we will yield the
                //same thing

                yield return _current.Current;
                if(_current.Current!=null)
                Debug.Log(_current.Current.ToString());//wait for seconds OR null

            }
            else
                //Otherwise wait for the next frame
                yield return null;
        }
    }
    
    IEnumerator CountUp()
    {
        //We have a local increment so the routines
        //get independently faster depending on how
        //long they have been active
        float increment = 0;
        while(true)
        {
            //Exit if the Q button is pressed
            if(Input.GetKey(KeyCode.Q))
                break;
            increment+=Time.deltaTime;
            value += Mathf.RoundToInt(increment);
            yield return null;
        }
    }
    
    IEnumerator CountDown()
    {
        float increment = 0f;
        while(true)
        {
            if(Input.GetKey(KeyCode.Q))
                break;
            increment+=Time.deltaTime;
            value -= Mathf.RoundToInt(increment);
            //This coroutine returns a yield instruction
            yield return new WaitForSeconds(0.1f);
        }
    }

}

  现在简单解释下该类。首先定义了三个IEnumerator接口类型的变量用来接受协程的返回值,在Start方法里面调用其中一个DoHijack()协程。这个协程里面有一个无限循环,在该循环里面检查_current是否赋值了,如果有值就调用该IEnumerator接口里面的MoveNext()方法,相当于每次循环都执行一遍_current对应的方法。至于_current.Current则返回了两个协程CountUp和CountDown的返回值,输出一下得到值为Null或WaitForSeconds(),恰恰是yield return后跟的返回值。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。