【问题标题】:Unity StopCoroutine does not stop coroutines when there are multi-layer (or nested) "yield return"Unity StopCoroutine 在有多层(或嵌套)“yield return”时不会停止协程
【发布时间】:2019-01-04 03:19:13
【问题描述】:

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

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协程??

【问题讨论】:

  • 你的 Unity 版本是什么?
  • 我的 Unity 版本是 5.6.4p3
  • 好的。看我的回答。只要您的第二个代码是您的实际代码,那么您的输出就是错误的。项目中有问题。请参阅我的回答,了解为什么会发生这种情况。如果您有任何问题,请在我的回答下留下

标签: c# unity3d coroutine


【解决方案1】:

对于您的第二个代码,日志确实应该以 Stop Coroutine.... 结尾。

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

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

2。您的项目已损坏,现在其中存在错误。有时,Unity 项目中可能会随机发生错误,解决此问题的唯一方法是创建一个新项目并手动将资源从旧项目移动到新项目。

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

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

使用以下问题中修改后的代码来测试 #2#3。它使用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;
    }
}

预期输出:

Enter FunA...

Enter FunB...

0

Stop Coroutine...

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

启动协程函数有两种方式

1。从任何函数(如返回类型为 void 的函数)开始使用 StartCoroutine 函数的协程。

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

IEnumerator Start()
{
    yield return StartCoroutine(RepeatPrint());
}

2。通过产生您要启动的协程函数来启动一个不带StartCoroutine 函数的协程函数。这必须在协程函数或具有IEnumerator 返回类型的函数内完成。它不能在像void 函数这样的普通函数中完成。

IEnumerator Start()
{
    yield return RepeatPrint();
}

当您使用 StartCoroutine 启动协程,然后使用 StartCoroutine 启动子协程但随后终止父协程时,子协程函数将并且应该继续不受影响地运行。

现在,当您使用 StartCoroutine 启动协程,然后在使用 yield return YourCoroutine() 没有 使用 StartCoroutine 函数启动子协程但随后终止父协程时,子协程函数将和应立即终止或停止,以作为父级。

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

【讨论】:

  • 我在 Unity 2017 和 Unity 2018 版本上测试了您的代码。它符合我对 2018 版本的期望,但它不适用于 2017 版本。所以根据你的猜测#3,这个bug属于Unity本身,Unity会在Unity 2018版本之后修复。
  • 还有一个发现:如果我添加一个层(即FunC()FunB()之后和RepeatPrint()之前),这个协程在2018版本上也不会停止
  • 我怀疑这是一个bug "如果我在FunB()之后和RepeatPrint()之前添加一个层(即FunC()),这个协程在2018年也不会停止version" 你能在“pastebin.com”上提供一个示例代码,然后在这里链接吗?我去看看,告诉你怎么了
  • 嗨@Programmer,示例代码在这里link。我在 Unity 2018.2.1f1 版本上测试了这段代码。
  • @Programmer yield return myFunction 在协程中并没有启动协程,它只是在调用 myFunction,就好像你只是将 myFunction 的代码放在协程中一样,而不调用 StartCoroutine,你不是在启动一个新的协程,而是只是调用一个函数,所以@KYL3R 是真的
【解决方案2】:

协程彼此独立运行。所以停止主协程不会影响其他已经启动的协程。

看来你有两个选择:

答:Stop the nested Coroutine from inside the first level

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

编辑:实际上“从第一级内部”不是必需的,只需使用正确的参考。

乙:Stop ALL Coroutines in your MonoBehaviour

  • 如果您没有其他协程在运行,可能会工作

【讨论】:

  • “所以停止主协程不会影响任何其他已经启动的协程。” 这并不完全正确。 StopCoroutine 应该杀死所有在其中运行的协程。
  • 文档没有说什么。我发现了一个论坛条目,指出它们是 independent - 如果您有任何来源可以阐明停止嵌套协程上的行为,请链接它。
  • 是的,它没有记录在案,但你所说的并不完全正确。有些部分是真的,有些则不是。我想留下一个答案来解释原因,但在这样做之前需要了解 OP 的 Unity 版本
  • 嗨@Programmer,我的Unity版本是5.6.4p3
猜你喜欢
  • 2016-06-15
  • 1970-01-01
  • 2019-06-19
  • 2018-04-18
  • 2014-02-12
  • 2021-11-27
  • 1970-01-01
  • 1970-01-01
  • 2016-06-14
相关资源
最近更新 更多