在Unity中使用协同程序通常是解决某些问题的好方法,但它也有一些缺点:
1.协同程序无法返回值。这鼓励程序员创建巨大的单片协程,而不是用许多小方法编写它们。存在一些变通方法,例如将Action <>类型的回调参数传递给协同程序,或者在协程完成后转换从协同程序产生的最终无类型值,但这些方法使用起来很容易并且容易出错。
2.协同程序使错误处理变得困难。您不能将yield放在try-catch中,因此无法处理异常。此外,当异常确实发生时,堆栈跟踪仅告诉您抛出异常的协同程序,因此您必须猜测它可能从哪个协程调用。
随着Unity 2017的发布,现在可以使用名为async-await的新C#功能代替我们的异步方法。与协同程序相比,它具有许多不错的功能。
要启用此功能,您只需打开播放器设置(File - >Build Settings - >Player Settings.. ->Other Settings )并将“Scripting Runtime Version“改为(.NET 4.x)”。
我们来看一个简单的例子。鉴于以下协程:
public class AsyncExample : MonoBehaviour
{
IEnumerator Start()
{
Debug.Log("Waiting 1 second...");
yield return new WaitForSeconds(1.0f);
Debug.Log("Done!");
}
}
使用async-await执行此操作的等效方法如下:
public class AsyncExample : MonoBehaviour
{
async void Start()
{
Debug.Log("Waiting 1 second...");
await Task.Delay(TimeSpan.FromSeconds(1));
Debug.Log("Done!");
}
}
在这两种情况下,有点意识到引擎盖下发生了什么是有帮助的。
简而言之,Unity协程是使用C#对迭代器块的内置支持实现的。您提供给StartCoroutine方法的IEnumerator迭代器对象由Unity保存,每个帧此迭代器对象向前推进以获取由您的协同程序产生的新值。然后,Unity会读取“返回”的不同值以触发特殊情况行为,例如执行嵌套协程(返回另一个IEnumerator时),延迟一些秒(当返回WaitForSeconds类型的实例时),或者只是等到下一帧(返回null时)。
不幸的是,由于async-await在Unity中是一个非常新的事实,如上所述的这种对协同程序的内置支持并不像async-await那样以类似的方式存在。这意味着我们必须自己添加很多这种支持。
Unity确实为我们提供了一个重要的部分。正如您在上面的示例中所看到的,默认情况下,我们的异步方法将在主Unity线程上运行。在非统一C#应用程序中,异步方法通常自动在不同的线程上运行,这在Unity中是一个很大的问题,因为在这些情况下我们无法始终与Unity API进行交互。如果没有Unity引擎的支持,我们在异步方法中对Unity方法/对象的调用有时会失败,因为它们将在一个单独的线程上执行。它的工作原理是这样,因为Unity提供了一个名为UnitySynchronizationContext的默认SynchronizationContext,它自动收集每帧排队的任何异步代码,并继续在主统一线程上运行它们。
然而,事实证明,这足以让我们开始使用async-await!我们只需要一些辅助代码就可以让我们做一些有趣的事情,而不仅仅是简单的时间延迟。
定制Awaiters
目前,我们无法编写很多有趣的异步代码。我们可以调用其他异步方法,我们可以使用Task.Delay,就像上面的例子一样,但不是很多。
举个简单的例子,让我们添加直接'等待'在TimeSpan上的能力,而不是每次都像上面的例子一样调用Task.Delay。像这样:
public class AsyncExample : MonoBehaviour
{
async void Start()
{
await TimeSpan.FromSeconds(1);
}
}
我们需要做的就是只需向TimeSpan