【问题标题】:Reset a repeating Coroutine delay重置重复的协程延迟
【发布时间】:2018-10-13 23:22:11
【问题描述】:

我正在使用协程来设置重复延迟,如下所示。

在我清醒时我有

StartCoroutine(RandomMove());

然后再往下

IEnumerator RandomMove()
    {
       while (true)
        {
            // print(Time.time);
            yield return new WaitForSeconds(foo);
            // print(Time.time);
        }

    }

其中 'foo' 是一个随机浮点值,我在每次迭代时都会更改。 假设 foo 是 10 秒,并且在延迟的中途我需要重置延迟,以便它再次从 10 开始“倒计时”。

我将如何做到这一点?我应该改用计时器吗?

【问题讨论】:

  • 你能再举一个例子吗?我不太清楚你想要完成什么。如果您试图在foo 时间到期之前“重置”延迟,您可能需要再次停止并启动协程。或者您只是想弄清楚如何在每次迭代中创建一个新变量?

标签: c# unity3d coroutine


【解决方案1】:

我不喜欢现有的两个答案中的任何一个。这是我要做的:

杀死并重启协程:

我们将从killer_mech的答案的这一部分开始:

Coroutine myCoroutine;
void Awake() {
    myCoroutine = StartCoroutine(RandomMove());
}

但我们将以不同的方式处理其余部分。 Killer_mech 从来没有任何对引用的操作,除了不断覆盖它。

这就是我们正在做的事情:

public void resetRandomMove() {
    StopCoroutine(myCoroutine);
    myCoroutine = StartCoroutine(RandomMove());
}

在需要重置时随时调用。

【讨论】:

  • 不错!并且在游戏运行后第一次调用协程(不是在Awake中),可以调用resetRandomMove并将第一行更改为:if (myCoroutine != null) { StopCoroutine(myCoroutine); }
【解决方案2】:

我建议您首先将 Coroutine 存储在一个变量中。

Coroutine myCoroutine;
void Awake()
{
 myCoroutine = StartCoroutine(RandomMove());
}

并将协程函数更改为

IEnumerator RandomMove()
{
            // print(Time.time);
            yield return new WaitForSeconds(foo);
            // print(Time.time);
            // Call your new coroutine here
            myCoroutine = StartCoroutine(RandomMove());
}

这样你每次迭代都会有一个协程变量。如果您需要停止协程,只需说:

StopCoroutine(myCoroutine);

在需要的时间在你的函数中。这将允许你在倒计时结束之前在中间停止一个协程。同样在协程结束时,它将使用更新的参考启动新的协程完成任务后,只需再次调用

myCoroutine = StartCoroutine(RandomMove());

希望这能解决您的问题。是的,你也可以用计时器来做同样的事情,但我认为使用协程要简单得多。 .

【讨论】:

  • 出于各种原因,我想指出,存储IEnumerator 引用比存储Coroutine 引用更好。
  • @Galandil 存储IEnumerator 引用的“各种原因”是什么?
  • @sonny 使用IEnumerator 在代码中更易于管理/读取,因为您可以在协程启动之前将引用分配到任何位置。此外,您可以使用 IEnumerator 的所有方法和属性,即使 Unity 中的这一点仅限于标准协程的 MoveNext() - 但是您可以创建继承自 IEnumerator 的类,并且可以实现 @987654332 @ 和 Current(如果是这种情况,所有代码都会读起来更好,而不是混合使用 CoroutineIEnumerator 引用)。
  • 另一件事是,对于带参数的协程,你可以有一个StartCoroutine(IEnum reference),并且在调用协程之前有多个不同参数的引用分配:Coroutine你每次都需要用特定的参数启动协程。
  • 我第一次研究这个问题的时候也真的很困惑。 Coroutine 类型引用只能在启动协程时使用,并且仅对停止该协程有用。另一方面,IEnumerator 类型,正如我所说,它不仅对停止协程有用,而且对我解释的所有其余部分都有用。而且我忘了提到您可以将其设为public,这意味着您甚至可以在实际启动协程的类之外设置该引用。基本上,我发现Coroutine 是一个真正没用的类型,因为它没有给IEnumerator 增加任何东西。
【解决方案3】:

嗯,它也可以是这样的。只为我自己。

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

IEnumerator RepeatingFunction () {
yield return new WaitForSeconds(repeatTime);

StartCoroutine( RepeatingFunction() );
}

据我了解这个问题。 InvokeRepeating()也是一种选择。

【讨论】:

    【解决方案4】:

    也许是因为你每一帧都在等待新分配的秒数?

    为什么不在产生等待之前进行随机化,并存储 CustomYieldInstruction 而不是产生一个新实例,因为它会处理以前的实例,这会产生内存问题。您不会注意到,如果您 yield 返回一个具有恒定值的 WaitForSeconds,但可能是一个随机值会产生歧义并重置计时器(请参阅此 Unity's optimization page, on the Coroutine's section)。一个简单的例子:

    public float foo;
    public float min;
    public float max;
    
    void Awake()
    {
        StartCoroutine(Countdown());
    }
    
    IEnumerator Countdown()
    {
        while(true)
        {
            foo = Random.Range(min, max);
            WaitForSeconds wait = new WaitForSeconds(foo);
            yield return wait;
        }
    }
    

    另外,@ryeMoss 的解决方案似乎是一个不错的解决方案,如果 'foo' 发生变化,则停止并重新启动协程。

    希望对你有帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-29
      • 2013-02-17
      • 2018-07-11
      • 1970-01-01
      相关资源
      最近更新 更多