繁体   English   中英

协程和范围

[英]Coroutines and scope

收到事件后,使用协程程序进行延迟。 延迟之后,我应该将私有布尔值'_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!!
}

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

但这不是您的代码无法正常工作的原因,这是因为您从未在第一次调用后创建新的协程,而是总是尝试重新启动相同的协程实例。

你应该做这个:

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 = MyDelay(); 用于创建对同一IEnumerator方法的另一个实例的新引用,以便在启动该实例时从头开始启动它,并尝试重新启动已经结束并且完全不执行任何操作的实例。

无论如何我都会移动_isActive = true; MyDelay()的开头,因此很明显,通过仅读取该方法,协程将执行的操作。

编辑:我将尝试解释为什么您需要创建一个新的引用才能使其正常工作。

Unity中的IEnumerator只有两个可访问且有效的元素: MoveNext()CurrentReset()不起作用)。 多亏了MoveNext() ,Unity才能使协程正常工作。

IEnumerator工作方式如下:它是对象的集合,只能按顺序访问:第一次初始化时, Current指向第一个对象之前的元素,然后访问下一个元素,您需要调用MoveNext() , 等等。 在Unity之外,您可以使用Reset()重置当前对象(使其返回第一个元素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()第一个MoveNext() ,因此Current成为Do Stuff #1并被执行
  • yield return null这时,指示Unity中断代码的执行,直到通过一个帧为止,该执行将在下一个Update回调的末尾开始
  • 在执行下一个Update()之后,Unity在MyCoroutine()上执行MoveNext() MyCoroutine() ,这将使Current等于Do Stuff 2并执行
  • yield return new WaitForSeconds(1f); 被击中-现在指示Unity在1秒后执行MoveNext() (因此它将通过使用Time.deltaTime检查时间差来尝试使其适合两个Unity回调之间的时间)
  • 1秒后, MoveNext() ,并Do Stuff #3

现在协程已经结束,但如果您通过引用启动协程,则IEnumerator不会永远消失。 因为除非重新分配引用,否则引用将保持不变,因此即使您尝试使用StartCoroutine(reference) ,Unity也会立即在该枚举器上执行MoveNext() ,但是在您击中的最后一个Current之后没有元素,因此它什么也不会执行,因为什么都没有执行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM