问候。
我现在面临一个问题,我的coroutine无法启动。这是我第一次面临这个问题,我在网上找不到合适的解决方案。如果有谁能给我指出解决这个问题的正确方向,我将非常感激。
下面是代码。
path_reference.GetDownloadUrlAsync().ContinueWith((Task<Uri> task) => {
if (!task.IsFaulted && !task.IsCanceled)
{
Debug.Log("Download URL: " + task.Result);
StartCoroutine(DownloadStuff(task.Result));
}
else
{
Debug.Log(task.Exception.ToString());
}
});
}
IEnumerator DownloadStuff(Uri uri)
{
Debug.Log("Start Download");
using (var www = UnityWebRequestTexture.GetTexture(uri))
{
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
Debug.Log(www.error);
}
else
{
var texture = DownloadHandlerTexture.GetContent(www);
//Texture2D texture = new Texture2D(1, 1);
//if you need sprite for SpriteRenderer or Image
Sprite sprite = Sprite.Create(texture, new Rect(0.0f, 0.0f, texture.width,
texture.height), new Vector2(0.5f, 0.5f), 100.0f);
Debug.Log("Finished downloading!");
}
Debug.Log(www.downloadProgress);
}
}'
Firebase返回的任务可能是在主线程以外的线程上完成执行的,而Unity的coroutine只能在主线程上运行。
Unity对多线程和异步的支持非常不到位,包括 "吃掉 "一些错误,如果这些错误的延续会在主线程以外的另一个线程上执行。
要解决这个问题,你需要改变启动你的coroutine的函数。
try {
// Important: ConfigureAwait(true) ensures the code after this will run in the
// same thread as the code before (which is the main thread!)
var url = await path_reference.GetDownloadUrlAsync().ConfigureAwait(true);
StartCoroutine(DownloadStuff(url));
} catch (Exception ex) {
// Tip: when logging errors, use LogException and pass the whole exception,
// that way you will get pretty links to the error line in the whole stack trace.
Debug.LogException(ex);
}
顺便说一下,我通常在我所有的项目中都有一些扩展方法来处理这个问题,而不是呆在async世界而不是coroutine世界(因为至少在async中我可以捕捉到错误,而不是像Unity的coroutine那样 "停止并着火")。
主要的是。
public static Task ToTask(this YieldInstruction self, MonoBehaviour owner) {
var source = new TaskCompletionSource<object>();
IEnumerable Routine() {
yield return self;
source.SetResult(null);
}
return source.Task;
}
private static Task SendAsync(this UnityWebRequest self, MonoBehaviour owner) {
var source = new TaskCompletionSource<object>();
await self.SendWebRequest().ToTask(owner);
if (
self.isHttpError
|| self.isNetworkError
) {
source.SetException(new Exception(request.error));
yield break;
}
source.SetResult(null);
}
你可以这样使用,在一个 MonoBehaviour
:
await new WaitForSeconds(0.2f).ToTask(this);
UnityWebRequest request = /* etc */;
await request.SendAsync(this);
var texture = DownloadHandlerTexture.GetContent(request);
注意,这些方法不需要 ConfigureAwait
由于他们的 SetResult
SetException
调用是由Unity提供的coroutine延续运行的。