协同程序和范围

时间:2018-05-10 19:56:22

标签: c# unity3d

在收到事件后使用协程延迟。 延迟后,我应该将一个私有布尔'_isActive'重置为false,但这种情况永远不会发生。 这可能是由于范围问题造成的吗?

这是我的代码

using UnityEngine;
using System.Collections;
using System;

IEnumerator coroutine;
bool _isActive;

void Awake()
{
    _isActive = false;
    coroutine = MyDelay();
}

private bool MyEventhandler(IEvent evt)
{
    StartCoroutine(coroutine);
    _isActive = true;
    Debug.Log("Yes event received");
}

IEnumerator MyDelay()
{
    yield return new WaitForSeconds(2.0f);
    Debug.Log("Yes delay complete");
    _isActive = false;//private class variable _isActive does not get reset to false
}


void Update(){
    Debug.Log(" _isActive = " + _isActive);//_isActive gets set to true but never gets reset to false!!
}

1 个答案:

答案 0 :(得分:2)

对于初学者,您的代码无法编译,您在bool中未返回MyEventhandler值。

但这并不是你的代码无法运行的原因,而是因为你在第一次调用后从未创建 new 协程,你总是尝试重新启动相同的协程实例。

你应该这样做:

private bool MyEventhandler(IEvent evt) {
    StopCoroutine(coroutine); //OPTIONAL - read below
    coroutine = MyDelay();
    StartCoroutine(coroutine);
    _isActive = true;
    Debug.Log("Yes event received");
    return _isActive;
}

StopCoroutine(coroutine);用于停止先前运行的协同程序,因此每次引发事件时,延迟都将重置为2秒。如果您不想在延迟计时器结束前重置它,即使引发了其他事件,也不要停止Coroutine。

然后,coroutine = MyDelay();用于创建对同一IEnumerator方法的另一个实例的新引用,因此当您启动它时,您将从头开始并尝试重新启动实例这已经到了最后,根本不会做任何事情。

我会在_isActive = true;的开头移动MyDelay(),所以很清楚,通过只阅读该方法,该协程会做什么。

编辑:我将尝试解释为什么你需要创建一个新的引用来使它工作。

Unity中的

IEnumerator只有两个可访问且有效的元素:MoveNext()CurrentReset()不起作用)。 这要归功于Unity MoveNext()可以使协程工作。

IEnumerator以这种方式工作:它是一组对象,只能按顺序访问:第一次初始化时,Current指向第一个对象之前的元素,然后访问在下一个元素中,您需要调用MoveNext(),依此类推。在Unity之外,您可以使用Reset()重置当前对象(使其返回到第一个元素),但正如我所说,这在Unity中不起作用。

这就是在Unity中执行协同程序的方式:在启动时,Unity会执行所有代码,直到它到达yield return指令 - 这告诉Unity需要重新启动执行剩余代码的点。基本上,如果您有这样的代码:

IEnumerator MyCoroutine() {
    // Do stuff #1
    yield return null;
    //Do stuff #2
    yield return new WaitForSeconds(1f);
    //Do stuff #3
}

这是怎么回事:

  • Coroutine()StartCoroutine开头:此时,枚举数位于第一个元素之前
  • 在枚举器上调用了第一个MoveNext(),因此Current成为Do Stuff #1并被执行
  • yield return null - 此时,Unity被指示中断代码的执行,直到帧通过,执行将在下一个Update回调结束时开始
  • 执行下一个Update()后,Unity会在MoveNext()上生成MyCoroutine(),这会使Current等于Do Stuff 2,然后执行< / LI>
  • yield return new WaitForSeconds(1f);被击中 - 现在Unity被指示在1秒后执行MoveNext()(因此它会尝试通过使用{{1}检查时间差来使它适合两个Unity的回调之间})
  • 1秒钟通过,Time.deltaTime被调用,MoveNext()被执行。

现在协程已经到了尽头,但Do Stuff #3并没有好消失,如果你通过引用启动协程。因为引用保持不变,除非再次分配,所以即使您尝试使用IEnumerator,Unity也会立即在该枚举器上执行StartCoroutine(reference),但在最后{{1}之后没有元素你点击了,所以它不会执行任何东西,因为没有什么可以执行。