当存在多层(或嵌套的)“收益回报”时,Unity StopCoroutine不会停止协程

时间:2018-07-27 03:47:46

标签: c# unity3d unityscript coroutine

如果我使用一层yield return,则StopCoroutine()可以成功停止我的协程。请参见下面的代码示例...

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

public class TestStopcoroutine : MonoBehaviour {
  IEnumerator co = null;

  // Use this for initialization
  void Start () {
    co = FunA();
    StartCoroutine(co);
  }

  private IEnumerator FunA() {
    Debug.Log("Enter FunA...");
    yield return RepeatPrint();
    Debug.Log("FunA end...");
  }

  private IEnumerator RepeatPrint() {
    for (int i = 0; i < 5; i++) {
      Debug.Log(i);
      yield return new WaitForSeconds(1);
    }
  }

  /// <summary>
  /// Set this function to a button on UI Canvas 
  /// </summary>
  public void OnCancelButtonClick() {
    if (co != null) {
      StopCoroutine(co);
      Debug.Log("Stop Coroutine...");
      co = null;
    }
  }
}

此输出为...

// Enter FunA...
// 0
// 1
// 2
// 3
// Stop Coroutine...

但是,如果我添加一层(即FunB()),FunA()将停止,但内部协程(FunB())不会停止。请参见下面的示例代码:

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

public class TestStopcoroutine : MonoBehaviour {
  IEnumerator co = null;

  void Start () {
    co = FunA();
    StartCoroutine(co);
  }

  private IEnumerator FunA() {
    Debug.Log("Enter FunA...");
    yield return FunB();
    Debug.Log("FunA end...");
  }

  private IEnumerator FunB () {
    Debug.Log("Enter FunB...");
    yield return RepeatPrint();
    Debug.Log("FunB end...");
  }

  private IEnumerator RepeatPrint() {
    for (int i = 0; i < 5; i++) {
      Debug.Log(i);
      yield return new WaitForSeconds(1);
    }
  }

  /// <summary>
  /// Set this function to a button on UI Canvas 
  /// </summary>
  public void OnCancelButtonClick() {
    if (co != null) {
      StopCoroutine(co);
      Debug.Log("Stop Coroutine...");
      co = null;
    }
  }
}

此输出为...

// Enter FunA...
// Enter FunB...
// 0
// 1
// 2
// Stop Coroutine...
// 3
// 4
// FunB end...

因此,我想知道为什么StopCoroutine()无法成功停止多层yield return协程?

2 个答案:

答案 0 :(得分:1)

对于第二个代码,日志确实应该以{{1​​}}结尾。

为何显示当前输出有三种可能性:(很可能是这种情况发生的原因)

1 。您正在将Stop Coroutine....设置为Time.timeScale。在整个项目中进行搜索,并确保您没有执行此操作:0。这可能会暂停或唤醒Time.timeScale = 0;正在等待的协程函数。如果这样做,请暂时将其删除或注释掉,看看是否是问题所在。

2 。您的项目已损坏,现在有一个错误。有时,Unity项目中可能会随机发生一个错误,并且修复该错误的唯一方法是创建一个新项目,然后将资源从旧项目手动移至新项目。

创建一个新项目并在下面测试修改后的代码。

3 。 Unity本身的错误。由于您使用的是 Unity 5.6.4p3 ,因此很有可能是Unity的错误。如果执行#1 #2 中的操作不能解决您的问题,则只需将Unity更新到最新版本(Unity 2018.xxx)。这样更有可能解决您的问题,并且别忘了用新项目进行测试,而不是导入旧项目。

使用下面问题中的修改后的代码来测试#2 #3 。它使用WaitForSeconds函数在秒后停止协程。

Invoke

预期输出:

IEnumerator co = null;

void Start()
{
    co = FunA();
    Invoke("OnCancelButtonClick", 1f);
    StartCoroutine(co);
}

private IEnumerator FunA()
{
    Debug.Log("Enter FunA...");
    yield return FunB();
    Debug.Log("FunA end...");
}

private IEnumerator FunB()
{
    Debug.Log("Enter FunB...");
    yield return RepeatPrint();
    Debug.Log("FunB end...");
}

private IEnumerator RepeatPrint()
{
    for (int i = 0; i < 5; i++)
    {
        Debug.Log(i);
        yield return new WaitForSeconds(1);
    }
}

/// <summary>
/// Set this function to a button on UI Canvas 
/// </summary>
public void OnCancelButtonClick()
{
    if (co != null)
    {
        StopCoroutine(co);
        Debug.Log("Stop Coroutine...");
        co = null;
    }
}

要解决KYL3R's的回答,即停止主协程不会影响任何其他已经启动的协程。这部分是正确的,但完全取决于协程的启动方式和启动位置。

有两种方法可以启动协程功能

1 。从Enter FunA... Enter FunB... 0 Stop Coroutine... 函数的任何函数(例如返回类型为StartCoroutine的函数)开始使用协程。

void

void Start()
{
    StartCoroutine(RepeatPrint());
}

2 。通过产生要启动的协程函数,启动IEnumerator Start() { yield return StartCoroutine(RepeatPrint()); } 函数的协程函数。这必须在协程函数或返回类型为StartCoroutine的函数内完成。无法使用IEnumerator之类的普通函数来完成。

void

当您从IEnumerator Start() { yield return RepeatPrint(); } 开始协程,然后从StartCoroutine开始子协程,但又杀死了父协程时,子协程功能将并且应该继续以保持原样运行

现在,当您使用StartCoroutine函数开始使用StartCoroutine进行协程时,然后使用yield return YourCoroutine()函数使用StartCoroutine 来启动子协程,然后杀死父协程,儿童协程将立即并且应该终止或停止,作为父级。

大多数Unity用户不知道并不奇怪,因为它没有记录,但是在Unity中使用协程时,了解这一点非常重要。

答案 1 :(得分:0)

协程彼此独立运行。因此,停止主协同程序不会影响其他已经启动的协同程序。

似乎您有2个选择:

A:Stop the nested Coroutine from inside the first level

  • 存储对RepeatPrint的引用,并使用 that 通过StopCoroutine停止引用。

编辑:实际上没有必要“从第一级内部”,只需使用正确的参考即可。

B:Stop ALL Coroutines in your MonoBehaviour

  • 如果没有其他协同程序在运行,可能会起作用