Unity Coroutine yield return null EQUIVALENT with Task async await
Unity Coroutine yield return null EQUIVALENT with Task async await
在异步方法中,Coroutine 中的 yield return null;
(更新时的每一帧 运行)等效于什么?
我找到的最近的是 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 ?
}
}
目前没有 yield return null
的等效方法。
我想说这是不可能的,因为异步可以在主 Thread
之外的另一个 Thread
中调用,主 Thread
可能会抛出异常,因为您不能使用 Unity 的 API在另一个线程中,但它 looks 像 Unity 通过在 Unity 5.6.0b5 及更高版本中实现自己的异步上下文来解决线程问题。
仍然可以做到,但您必须自己实施或使用现有的 API。 UnityAsync
API 已经可以做到这一点。你可以得到它here。 NextUpdate
函数替换了 yield return null
指令。
示例:
你常用的协程代码:
private IEnumerator RunEachFrame()
{
while (true)
{
print("Run Each frame right before rendering");
yield return null;
}
}
等效的异步代码:
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();
}
}
}
注意脚本如何继承自 AsyncBehaviour
而不是 MonoBehaviour
。
如果你真的想继承MonoBehaviour
而不是AsyncBehaviour
并且仍然使用这个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
}
}
}
以下是完整支持的等待函数:
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
class 中找到,以防它们被重命名或删除。
如果您 运行 对此 API 有任何疑问,请参阅 Unity 的论坛 post 并在那里提问。
至少在 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);
}
}
}
会给你输出:
Frame: 1
Frame: 2
Frame: 3
...
似乎如果 Debug.Log("Frame: " + Time.frameCount);
行在 await Task.Yield();
之前,它会在第一帧中 运行 两次。我不确定这是什么原因。
使用 UniTask library 的 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();
}
}
}
首先,定义一个创建异步任务并使用协程等待帧结束的函数。
Task<bool> WaitOneFrameAsync()
{
var tcs = new TaskCompletionSource<bool>();
StartCoroutine(WaitOneFrame(tcs));
return tcs.Task;
}
static IEnumerator WaitOneFrame(TaskCompletionSource<bool> tcs)
{
yield return new WaitForEndOfFrame();
tcs.TrySetResult(true);
}
现在你可以愉快的在async函数中使用了
await WaitOneFrameAsync();
在异步方法中,Coroutine 中的 yield return null;
(更新时的每一帧 运行)等效于什么?
我找到的最近的是 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 ?
}
}
目前没有 yield return null
的等效方法。
我想说这是不可能的,因为异步可以在主 Thread
之外的另一个 Thread
中调用,主 Thread
可能会抛出异常,因为您不能使用 Unity 的 API在另一个线程中,但它 looks 像 Unity 通过在 Unity 5.6.0b5 及更高版本中实现自己的异步上下文来解决线程问题。
仍然可以做到,但您必须自己实施或使用现有的 API。 UnityAsync
API 已经可以做到这一点。你可以得到它here。 NextUpdate
函数替换了 yield return null
指令。
示例:
你常用的协程代码:
private IEnumerator RunEachFrame()
{
while (true)
{
print("Run Each frame right before rendering");
yield return null;
}
}
等效的异步代码:
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();
}
}
}
注意脚本如何继承自 AsyncBehaviour
而不是 MonoBehaviour
。
如果你真的想继承MonoBehaviour
而不是AsyncBehaviour
并且仍然使用这个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
}
}
}
以下是完整支持的等待函数:
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
class 中找到,以防它们被重命名或删除。
如果您 运行 对此 API 有任何疑问,请参阅 Unity 的论坛 post 并在那里提问。
至少在 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);
}
}
}
会给你输出:
Frame: 1
Frame: 2
Frame: 3
...
似乎如果 Debug.Log("Frame: " + Time.frameCount);
行在 await Task.Yield();
之前,它会在第一帧中 运行 两次。我不确定这是什么原因。
使用 UniTask library 的 UniTask.NextFrame
可以获得完全匹配 yield return null
的行为,这样您就不会在使用
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();
}
}
}
首先,定义一个创建异步任务并使用协程等待帧结束的函数。
Task<bool> WaitOneFrameAsync()
{
var tcs = new TaskCompletionSource<bool>();
StartCoroutine(WaitOneFrame(tcs));
return tcs.Task;
}
static IEnumerator WaitOneFrame(TaskCompletionSource<bool> tcs)
{
yield return new WaitForEndOfFrame();
tcs.TrySetResult(true);
}
现在你可以愉快的在async函数中使用了
await WaitOneFrameAsync();