任务等待异步的Unity Coroutine yield return null等效

Amb*_*ier 2 c# coroutine unity-game-engine async-await .net-4.6

yield return null;在异步方法中,Coroutine(在Update运行每帧)的等效项是什么?

我找到的最接近的是await Task.Delay(1);,但不要每帧都运行一次。

private IEnumerator RunEachFrame()
{
    while (true)
    {
        print("Run Each frame right before rendering");
        yield return null;
    }
}

async void DoNotRunEachFrame()
{
    while (true)
    {
        await Task.Delay(1); // What is the equivalent of yield return null here ?
    }
}
Run Code Online (Sandbox Code Playgroud)

Hem*_*lle 7

至少在 Unity 2018 中,您可以使用await Task.Yield(). 例如:

using System.Threading.Tasks;
using UnityEngine;

public class AsyncYieldTest : MonoBehaviour
{
    async void Start()
    {
        await Function();
    }
    
    async Task Function() {
        while (gameObject != null)
        {
            await Task.Yield();
            Debug.Log("Frame: " + Time.frameCount);
        }
    }  
}
Run Code Online (Sandbox Code Playgroud)

会给你输出:

Frame: 1
Frame: 2
Frame: 3
...
Run Code Online (Sandbox Code Playgroud)

似乎如果该Debug.Log("Frame: " + Time.frameCount);行在 之前await Task.Yield();,它将在第一帧中运行两次。我不确定这是什么原因。

随着UniTask库UniTask.NextFrame很可能获得匹配的行为yield return null完全让你不与第一门框上获得2个消息

using Cysharp.Threading.Tasks;
using UnityEngine;

public class AsyncYieldTest : MonoBehaviour
{
    async void Start()
    {
        await Function();
    }

    async UniTask Function() {
        while (gameObject != null)
        {
            // Debug.Log first like with yield return null
            Debug.Log("Frame: " + Time.frameCount);
            await UniTask.NextFrame();
        }
    }  
}
Run Code Online (Sandbox Code Playgroud)


Pro*_*mer 6

目前没有 for的等效方法yield return null

我要说这是不可能的,因为异步可以在Thread除主Thread线程之外的其他线程中调用,因为您不能在另一个线程中使用Unity的API,但是看起来 Unity通过在中实现自己的异步上下文来解决了线程问题Unity 5.6.0b5及更高版本。


仍然可以这样做,但是您必须自己实现或使用现有的API。该UnityAsyncAPI已经可以做到这一点。你可以在这里得到。该NextUpdate功能代替yield return null指令。

例子:

您通常的协程代码:

private IEnumerator RunEachFrame()
{
    while (true)
    {
        print("Run Each frame right before rendering");
        yield return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

等效的异步代码:

using UnityAsync;
using System.Threading.Tasks;

public class UpdateLoop : AsyncBehaviour
{
    void Start()
    {
        RunEachFrame();
    }

    // IEnumerator replaced with async void
    async void RunEachFrame()
    {
        while(true)
        {
            print("Run Each frame right before rendering");
            //yield return null replaced with await NextUpdate()
            await NextUpdate();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,脚本是如何继承AsyncBehaviour而不是的MonoBehaviour


如果您确实要继承MonoBehaviour而不是AsyncBehaviourAPI并仍然使用此API,请NextUpdate直接以调用该函数。以下Await.NextUpdate()是一个完整的等效示例:

using UnityAsync;
using System.Threading.Tasks;

public class UpdateLoop : MonoBehaviour
{
    async void Start()
    {
        await RunEachFrame();
    }

    async Task RunEachFrame()
    {
        while(true)
        {
            print("Run Each frame right before rendering");
            await Await.NextUpdate(); // equivalent of AsyncBehaviour's NextUpdate
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是完整的受支持的等待功能:

  • NextUpdate
  • NextLateUpdate
  • NextFixedUpdate
  • Updates(int framesToWait)
  • LateUpdates(int framesToWait)
  • FixedUpdates(int stepsToWait)
  • Seconds(float secondsToWait)
  • SecondsUnscaled(float secondsToWait)
  • Until(Func<bool> condition)
  • While(Func<bool> condition)
  • Custom(CustomYieldInstruction instruction)
  • AsyncOp(AsyncOperation op)

所有这些都可以在Await类中找到,以防万一它们被重命名或删除。

如果您在使用此API时遇到问题,请参阅Unity 专门针对该API的论坛帖子,并在那里提问。