协同(Coroutine)

介绍

  • 协同用来模拟线程,实现多任务。
  • 在协同中可以使用等待语句。

什么情况下使用协同

在以下3种情况下可以使用协同:

  • 当需要使用等待语句时,可以使用协同。
    // 等待语句:
    yield return new WaitForSeconds(1.5f);  //等待1.5秒
    yield return null; // 等待一帧
    
  • 当需要同时执行多个任务是,可以使用协同。
  • 把一个大任务拆分成多个耗时不长的小任务。

协同的使用

使用协同需要3个步骤

  1. 定义协同函数。协同函数的返回值必须为IEnumerator。参数类型和个数可以任意。
  2. 在协同函数中使用等待语句。
  3. 启动协同。使用StartCoroutine ()方法调用协同函数。
// 协同的使用
private GameObject[] monsters;
private Transform startPoint;
private Transform monsterList;

void Start () {
    // 开启协同
    StartCoroutine (Wave ());
}

// 出一波怪
IEnumerator Wave() {
    // 出10个怪
    for (int i = 0; i < 10; i++) {
        GenerateMonster ();

        // 等待1.5秒
        yield return new WaitForSeconds(1.5f);
    }
}

void GenerateMonster() {
    // 产生怪物    
}

协同的嵌套

协同可以嵌套,在一个协同中可以启动另一个协同。

// 协同的嵌套
void Start () {
    // 开启本关卡协同
    StartCoroutine (Level ());
}

// 本关卡出怪逻辑
IEnumerator Level() 
{
    // 出3波怪
    for (int i = 0; i < 3; i++) {
        StartCoroutine(Wave ());

        // 从出第一个怪开始,15秒后出下一波。
        yield return new WaitForSeconds (15f);
    }
}

// 出一波怪
IEnumerator Wave() {
    // 出10个怪
    for (int i = 0; i < 5; i++) {
        GenerateMonster ();

        // 等待1.5秒
        yield return new WaitForSeconds(1.5f);
    }
}

void GenerateMonster() {
    // 产生怪物    
}

对协同的理解

  • Unity是单线程的。如下图,Unity会顺序的调用Update协同LateUpdateMonoBehaviour生命周期
  • Unity在每个Update之后检查有没有要执行的协同,如果有就调用协同。
  • 协同函数使用yield return返回。当再次调用这个协同函数时,会从上次yield return的地方继续执行,而不是从函数开始的地方从头执行。
  • 协同用每一次yield return前执行代码的时间不要太长,这样会使后续的游戏GUI界面卡死。
  • 当启动协同时会立马调用协同函数,因为给StartCoroutine ()传参时调用了协同函数。

      void Start () {
          StartCoroutine(FunCoroutine ());
      }
    
      void Update () {
          Debug.Log ("Update");
      }
    
      IEnumerator FunCoroutine () {
          // yield return null;
    
          while (true) {
              Debug.Log(“Coroutine”);
              yield return null;
          }
      }
    

    运行结果如下:

     
    
  • 对比生命周期图,解释为什么yield return null;表示等待一帧。

  • 对比生命周期图,说明一下这个结果。其中第一个Update之后发现协同在本轮中已经执行了,就不会再执行协同函数。
  • 有些协同函数第一句代码就是yield return null;。这是为了跳过第一次协同函数的调用,保证协同函数一定在Update之后执行。

相关应用Demo

  • 等待3.5秒
      (代码和注释)
    
  • 等待一帧
      (代码和注释)
    
  • 等待10帧
      (代码和注释)
    
  • 等待条件成熟
      // 可以将这句话理解为:当条件不成熟时一直等待。
      (代码和注释)
    
  • 加载场景等模块
      (代码和注释)
    

相关参考

  • 实现等待