【问题标题】:Unity 'StartCoroutine' does not run twice when IEnumerator is passed as a variable?当 IEnumerator 作为变量传递时,Unity 'StartCoroutine' 不会运行两次?
【发布时间】:2020-05-19 16:17:31
【问题描述】:

所以我在 Unity 中使用 co-routine 模拟 NPC 时遇到了一些奇怪的事情(走向目标,闲置 x 秒,步行到一个目标——重复——)。

我发现使用包含 IEnumerator 的变量启动协程不会运行两次,而使用直接传入的方法启动协程会按预期运行,可重复。

为什么会这样? '引擎盖下'发生了什么?我无法理解为什么会这样,这让我很烦。

在模拟空闲时间的 IEnumerator 方法下方。

private IEnumerator sitIdle()
{
    var timeToWait = GetIdleTime();
    _isIdle = true;
    yield return new WaitForSeconds(timeToWait);
    _isIdle = false;
} 

如果在每个场景 #1(如下)中第二次调用它,它会在多次调用时按预期运行。它只是一遍又一遍地重复这个过程。

但是,如果它作为变量被每个场景 #2(如下)调用,它将启动一次,但拒绝第二次输入并在代码中简单地“跳过”它。

void LateUpdate()
    {
        _idleRoutine = sitIdle; //this is not actually in the late update, just moved here for reference.

        if (_agent.hasPath)
        {
            if (isTouchingTarget())
            {
                StartCoroutine(sitIdle2()); //Scenario #1

                StartCoroutine(_idleRoutine); //Scenario #2

                _currentTarget = null; 
                _agent.ResetPath();
            }
        }

Tl;dr:StartCoroutine(变量到 IEnumerator) 不可重复,而 StartCoroutine(IEnumerator()) 工作正常,为什么我不能将 IEnumerator 作为变量传递?

【问题讨论】:

  • 问题中显示的代码无法编译。你的意思是_idleRoutine = sitIdle();
  • StartCoroutine 是您的方法,还是 Unity 提供的?我的猜测是,如果它采用IEnumerator 的实例,它会通过在while 循环中调用MoveNext 来迭代IEnumerator。当您传递StartCoroutine 的结果sitIdle 时,您将传递IEnumerator 的一个新实例。但是,如果您将sitIdle 的结果放入变量中,那么一旦您调用StartCoroutine 一次,您就已经迭代到IEnumerator 的末尾。所以下次你将它传递给StartCoroutine 时,MoveNext 只会返回false
  • @JoshuaRobinson 是的,请参阅StartCoroutine

标签: c# unity3d coroutine


【解决方案1】:

SitIdle() 的返回值为IEnumeratorIEnumerators 一旦完成就不会重复并且没有剩余的迭代,这是StartCoroutine 告诉 Unity 做的。每次在 yield 处恢复协程时,Unity 都会告诉协程执行另一次迭代。

如果您真的想将协程存储为变量,以便您可以在其中一个或另一个之间进行选择,您可以将您感兴趣的方法存储为委托,然后调用它来获取新的IEnumerator

IEnumerator sitIdle()
{
    var timeToWait = GetIdleTime();
    _isIdle = true;
    yield return new WaitForSeconds(timeToWait);
    _isIdle = false;
}  

IEnumerator sitIdleAlternative()
{
    var timeToWait = GetIdleTime() + 2f;
    _isIdle = true;
    yield return new WaitForSeconds(timeToWait);
    _isIdle = false;
}

delegate IEnumerator IdleDelegate ();

IdleDelegate _idleRoutine;

void LateUpdate()
{
    _idleRoutine = new IdleDelegate(sitIdleAlternative); //this is not actually in the late update, just moved here for reference.

    _idleRoutine = new IdleDelegate(sitIdle);

    if (_agent.hasPath)
    {
        if (isTouchingTarget())
        {
            StartCoroutine(sitIdle2()); //Scenario #1

            StartCoroutine(_idleRoutine()); //Scenario #2

            _currentTarget = null; 
            _agent.ResetPath();
        }
    }
}

【讨论】:

  • 谢谢!这解决了我关于与 StartCoroutine 相关的“引擎盖下”的统一性的问题!
猜你喜欢
  • 2016-08-19
  • 1970-01-01
  • 1970-01-01
  • 2018-06-20
  • 1970-01-01
  • 2020-06-20
  • 2018-09-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多