unity3d Unity等待所有协程

rta7y2nd  于 2023-04-12  发布在  其他
关注(0)|答案(2)|浏览(288)

我将在另一个协程中等待几个协程。示例代码

private IEnumerator WaitForAllAnimations(List<ShiftedGem> shiftedGems)
{
    float duration = 3f;
    isDuringAnimation = true;

    List<Coroutine> animationCoroutines = new List<Coroutine>();

    for (int i = 0; i < shiftedGems.Count(); i++)
    {
        ShiftedGem shiftedGem = shiftedGems[i];
        Debug.Log("Add animation");
        ShiftAnimation shiftAnimation = shiftedGem.GameObject.AddComponent<ShiftAnimation>();
        yield return StartCoroutine(shiftAnimation.AnimateShift(shiftedGem.GameObject, shiftedGem.ShiftAmount, duration));

        animationCoroutines.Add(animationCoroutine);
    }

    foreach (Coroutine coroutine in animationCoroutines)
    {
        Debug.Log("Wait for coroutine");
        yield return coroutine;
    }

    Debug.Log("Animation end");
    isDuringAnimation = false;
}

但是,它并不像预期的那样工作-在AnimateShift完成之前调用Debug.log(“Animationend”)。
我尝试了一个更简单的例子:

IEnumerator CoroutineA(List<ShiftedGem> shiftedGems)
{
    Debug.Log("Start Coroutine A");

    yield return StartCoroutine(CoroutineB());
    Debug.Log("End Coroutine A");
}

IEnumerator CoroutineB()
{
    Debug.Log("Start Coroutine B");
    yield return new WaitForSeconds(1f);
    Debug.Log("End Coroutine B");
}

这工作正常(“End Coroutine A”记录在“End Coroutine B”之后)。
但是如果我将StartCoroutine(CoroutineB())移动到循环中:

IEnumerator CoroutineA(List<ShiftedGem> shiftedGems)
{
    Debug.Log("Start Coroutine A");
    foreach (ShiftedGem shiftedGem in shiftedGems)
    {
        yield return StartCoroutine(CoroutineB());
    }
    Debug.Log("End Coroutine A");
}

IEnumerator CoroutineB()
{
    Debug.Log("Start Coroutine B");
    yield return new WaitForSeconds(10f);
    Debug.Log("End Coroutine B");
}

然后再次-Debug.log(“End Coroutine A”)在Debug.log(“End Coroutine B”)之前调用。
我应该如何组织我的代码,以便能够等待WaitForAllAnimations协程中的所有animationCoroutines?

fd3cxomn

fd3cxomn1#

首先,你在协程列表中添加一个对象,我没有看到它被分配到任何地方,这看起来有点奇怪。无论如何,关于选项:
您可以使用某种计数器来计算当前运行的协程数量。基于您的最后一段代码:

int _someCounter = 0;

IEnumerator CoroutineA(List<ShiftedGem> shiftedGems)
{
    Debug.Log("Start Coroutine A");
    _someCounter = 0;
    foreach (ShiftedGem shiftedGem in shiftedGems)
    {
        StartCoroutine(CoroutineB());
    }
    while(_someCounter > 0)
    {
        yield return null;
    }
    Debug.Log("End Coroutine A");
}

IEnumerator CoroutineB()
{
    Debug.Log("Start Coroutine B");
    _someCounter++;
    yield return new WaitForSeconds(10f);
    _someCounter--;
    Debug.Log("End Coroutine B");
}

但是,请注意,如果第二次运行以上代码,直到上一次调用尚未完成,则上述代码将不稳定。
我认为对现有变体进行最少更改的最佳选择是在ShiftAnimation组件中添加一个标志,以检查动画是否仍在进行中。

private IEnumerator WaitForAllAnimations(List<ShiftedGem> shiftedGems)
{
    float duration = 3f;
    isDuringAnimation = true;

    List<ShiftAnimations> animations = new List<ShiftAnimations>();

    for (int i = 0; i < shiftedGems.Count(); i++)
    {
        ShiftedGem shiftedGem = shiftedGems[i];
        Debug.Log("Add animation");
        ShiftAnimation shiftAnimation = shiftedGem.GameObject.AddComponent<ShiftAnimation>();
        animations.Add(shiftAnimation);
        shiftAnimation.AnimateShift(shiftedGem.GameObject, shiftedGem.ShiftAmount, duration);

    }
    
    bool isAnyAnimationActive;
    do
    {
        isAnyAnimationActive = false;
        foreach (var animationIt in animations)
        {
            if(animationIt.IsAnimationActive == true)
            {
                isAnyAnimationActive = true;
                break;
            }
        }        
        
        yield return null;
    }
    while(isAnyAnimationActive == true);

    Debug.Log("Animation end");
    isDuringAnimation = false;
}

ShiftAnimation组件中,类似于这样的内容:

...
bool _isAnimationActive = false;

public IsAnimationActive => _isAnimationActive;

public void AnimateShift(your parameters here)
{
    StartCoroutine(CoAnimateShift(your parameters here));
}

IEnumerator CoAnimateShift(your parameters here)
{
    _isAnimationActive = true;

    // your code here

    _isAnimationActive = false;
}
...
kx5bkwkv

kx5bkwkv2#

如果你正在寻找一种更好的方式来处理Unity中的异步任务,我建议你看看UniTask plugin。这个插件提供了一个易于使用的替代传统的C# async/await,以及Unity的协程,这可能很难使用。
使用UniTask,您可以使用WhenAll()方法来等待多个任务完成。这种方法只需要将现有的协程重构为异步方法,这可以简化代码并提高其可读性。

相关问题