繁体   English   中英

无法理解延迟和协程

[英]Trouble understanding Delays and Coroutines

void start()
StartCoroutine(Text());

IEnumerator Text()
{
    Debug.Log("Hello")
    yield return new WaitForSeconds(3)
    Debug.Log("ByeBye")
}

我理解这样做的基本概念,但我不明白什么意思,例如 yield return new WaitforSeconds(3) 以及 StartCoroutine 是什么以及 IEnumerator 是什么。 任何人都可以向我解释他们的意思吗?

当你调用一个函数时,它会在返回之前运行到完成。 这实际上意味着函数中发生的任何动作都必须在单个帧更新中发生; 函数调用不能用于包含程序动画或随时间推移的一系列事件。 例如,考虑逐渐减小对象的 alpha(不透明度)值直到它完全不可见的任务。

void Fade() 
{
    for (float ft = 1f; ft >= 0; ft -= 0.1f) 
    {
        Color c = renderer.material.color;
        c.a = ft;
        renderer.material.color = c;
    }
}

就目前而言,淡入淡出功能不会达到您预期的效果。 为了使淡入淡出可见,必须在一系列帧上减少 alpha 以显示正在渲染的中间值。 但是,该函数将在单个帧更新中完整执行。 中间值永远不会被看到,对象会立即消失。 可以通过将代码添加到逐帧执行淡入淡出的更新函数来处理此类情况。 但是,对于此类任务,使用协程通常更方便。 协程就像一个函数,它能够暂停执行并将控制权返回给 Unity,然后在下一帧从它停止的地方继续。 在 C# 中,协程是这样声明的:

IEnumerator Fade() 
{
    for (float ft = 1f; ft >= 0; ft -= 0.1f) 
    {
        Color c = renderer.material.color;
        c.a = ft;
        renderer.material.color = c;
        yield return null;
    }
}

它本质上是一个用 IEnumerator 返回类型声明的函数,并且在函数体的某处包含了 yield return 语句。 yield return null 行是执行将暂停并在下一帧恢复的点。 要设置协程运行,需要使用 StartCoroutine 函数:

void Update()
{
    if (Input.GetKeyDown("f")) 
    {
        StartCoroutine("Fade");
    }
}

您会注意到 Fade 函数中的循环计数器在协程的整个生命周期内保持其正确的值。 事实上,任何变量或参数都将在产量之间正确保留。 默认情况下,协程在产生后在帧上恢复,但也可以使用 WaitForSeconds 引入时间延迟:

IEnumerator Fade() 
{
    for (float ft = 1f; ft >= 0; ft -= 0.1f) 
    {
        Color c = renderer.material.color;
        c.a = ft;
        renderer.material.color = c;
        yield return new WaitForSeconds(.1f);
    }
}

这可以用作在一段时间内传播效果的一种方式,但它也是一种有用的优化。 游戏中的许多任务需要定期执行,最明显的方法是将它们包含在更新函数中。 但是,此函数通常每秒会被调用多次。 当任务不需要如此频繁地重复时,您可以将其放入协程中以定期获取更新,但不是每一帧。 这方面的一个例子可能是警告玩家附近是否有敌人的警报。 代码可能如下所示:

bool ProximityCheck() 
{
    for (int i = 0; i < enemies.Length; i++)
    {
        if (Vector3.Distance(transform.position, enemies[i].transform.position) < dangerDistance) {
                return true;
        }
    }
    
    return false;
}

如果有很多敌人,那么每帧调用这个函数可能会带来很大的开销。 但是,您可以使用协程每十分之一秒调用一次:

IEnumerator DoCheck() 
{
    for(;;) 
    {
        ProximityCheck();
        yield return new WaitForSeconds(.1f);
    }
}

这将大大减少执行的检查次数,而不会对游戏玩法产生任何明显影响。 注意:您可以使用 StopCoroutine 和 StopAllCoroutines 停止协程。 当使用 SetActive(false) 禁用它所附加的 GameObject 时,协程也会停止。 调用 Destroy(example)(其中 example 是 MonoBehaviour 实例)立即触发 OnDisable 并处理协程,有效地停止它。 最后,在帧结束时调用 OnDestroy。 通过在 MonoBehaviour 实例上将 enabled 设置为 false 来禁用 MonoBehaviour 时,不会停止协程。

参考: https : //docs.unity3d.com/Manual/Coroutines.html

Unity (ab) 使用枚举器来构建 C# CoRoutine,因为 async / await 不存在。 当你写作时;

IEnumerator Text()
{
    Debug.Log("Hello")
    yield return new WaitForSeconds(3)
    Debug.Log("ByeBye")
}

编译器把它变成类似的东西;

IEnumerator Text() => new StateMachine();

public class StateMachine : IEnumerable{
    private int state = 0;
    // plus any local variables moved to fields.
    StateMachine(){}
    public object Current { get; set; }
    public bool MoveNext(){
        switch(state){
            case 0:
            Debug.Log("Hello");
            Current = new WaitForSeconds(3);
            state = 1;
            return true;

            case 1:
            Debug.Log("ByeBye");
            return false;
        }
    }
}

由于您的函数状态现在存储在对象的字段中,因此您的方法可以在完成之前暂停。 然后 Unity 将查看您生成的对象以决定何时调用MoveNext()

现在 C# 具有异步方法,这也会导致您的方法转换为状态机。 新版本的 unity 可能会支持它们,例如;

async Task Text()
{
    Debug.Log("Hello")
    await Something.WaitForSeconds(3)
    Debug.Log("ByeBye")
}

但是他们仍然必须支持构建 CoRoutine 的旧方式。

暂无
暂无

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

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