G4b*_*i3l 1 c# unity-game-engine async-await
我试图在 Unity 编辑器窗口上显示一个简单的图像,它工作了一段时间,然后我想我做了一些破坏它的更改,我不断收到 NullReferenceException。这是我的简单示例:
public class ShowImagePreview : EditorWindow
{
private Texture2D texture;
private async Task<Texture2D> GetHTTPTextureAsync(string imageUrl)
{
UnityWebRequest request = UnityWebRequestTexture.GetTexture(imageUrl);
await request.SendWebRequest();
if (request.isNetworkError || request.isHttpError)
{
Debug.Log("Something went wrong!");
Debug.Log(request.error);
return null;
}
return ((DownloadHandlerTexture)request.downloadHandler).texture;
}
private async Task OnGUI()
{
GUILayout.BeginHorizontal("box");
if (texture != null)
{
GUILayout.Label("Texture is not null!");
GUILayout.Box(texture, GUILayout.Width(140), GUILayout.Height(100));
}
else
{
GUILayout.Label("Texture is NULL!");
try
{
// This is what's causing the issue and keeping the texture value to null
texture = await GetHTTPTextureAsync("https://nikkorpon.files.wordpress.com/2011/02/random-shots-059.jpg");
}
catch (Exception e)
{
Debug.Log(e);
}
}
GUILayout.EndHorizontal();
}
}
Run Code Online (Sandbox Code Playgroud)
这是从一个更大的脚本中摘录的。图片 url 实际上是动态的,但为了这个例子,我只是抓取了一个随机的图片 url。我正在使用 Async/Await 插件,它可以按预期处理任何其他请求。知道我可能做错了什么吗?
这是完整的错误消息:
System.NullReferenceException: Object reference not set to an instance of an object
at IEnumeratorAwaitExtensions.RunOnUnityScheduler (System.Action action) [0x00042] in /Users/user_name/Projects/project_name/Assets/Plugins/AsyncAwaitUtil/Source/IEnumeratorAwaitExtensions.cs:128
at IEnumeratorAwaitExtensions.GetAwaiterReturnSelf[T] (T instruction) [0x00025] in /Users/user_name/Projects/project_name/Assets/Plugins/AsyncAwaitUtil/Source/IEnumeratorAwaitExtensions.cs:115
at IEnumeratorAwaitExtensions.GetAwaiter (UnityEngine.AsyncOperation instruction) [0x00002] in /Users/user_name/Projects/project_name/Assets/Plugins/AsyncAwaitUtil/Source/IEnumeratorAwaitExtensions.cs:55
at ShowImagePreview+<GetHTTPTextureAsync>c__async0.MoveNext () [0x00033] in /Users/user_name/Projects/project_name/Assets/Resources/Scripts/Import.cs:18
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <f2e6809acb14476a81f399aeb800f8f2>:0
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <f2e6809acb14476a81f399aeb800f8f2>:0
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <f2e6809acb14476a81f399aeb800f8f2>:0
at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <f2e6809acb14476a81f399aeb800f8f2>:0
at System.Runtime.CompilerServices.TaskAwaiter`1[TResult].GetResult () [0x00000] in <f2e6809acb14476a81f399aeb800f8f2>:0
at ShowImagePreview+<OnGUI>c__async1.MoveNext () [0x0010b] in /Users/user_name/Projects/project_name/Assets/Resources/Scripts/Import.cs:43
UnityEngine.Debug:Log(Object)
<OnGUI>c__async1:MoveNext() (at Assets/Resources/Scripts/Import.cs:47)
System.Runtime.CompilerServices.AsyncTaskMethodBuilder:Start(<OnGUI>c__async1&)
ShowImagePreview:OnGUI()
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)
Run Code Online (Sandbox Code Playgroud)
找到罪魁祸首了!
代码本身工作得很好,没有错误。主要问题(感谢@KevinGosse 的一些挖掘)是由 Async/Await 插件代码触发的。
一旦我将调试器放在IEnumeratorAwaitExtensions.cs 的第 128 行,我注意到它SyncContextUtil.UnitySynchronizationContext为空,这就是导致NullReference错误的原因。
该SyncContextUtil.UnitySynchronizationContext是在initilized SyncContextUtil.cs脚本文件归功于Install方法。所以问题是,为什么这个方法没有被触发?
查看源代码,您可以看到装饰器[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)],它Install在触发播放模式时简单地调用该方法!但是我在编辑器模式下工作,因此无论我做什么,该Install方法都不会触发并且SyncContextUtil.UnitySynchronizationContext仍然null存在。
原来这个问题在 repo 中是已知的,你可以在这里查看。他们针对这个确切的问题提出了一个解决方案,以允许在编辑器模式下使用 Async/Await。
对我来说,它为什么在某些时候起作用非常令人费解,然后我又没有对代码进行任何更改。原因是我激活了播放模式一秒钟,然后停止了它并继续在编辑器模式下工作。正如我们在启动播放模式之前提到的,会调用Install使一切正常工作的方法。然后我关闭了 Unity,重新启动它并且(没有激活播放模式)我的脚本都不再工作了!
我现在将按照问题评论中建议的方法使 Async/Await 在编辑器模式下也按预期工作。我希望这个答案也能帮助其他人!
编辑
我向该Install方法添加了一个装饰器,并且不再出现错误。这是修改后的代码:
namespace UnityAsyncAwaitUtil
{
public static class SyncContextUtil
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
[InitializeOnLoadMethod]
static void Install()
{
// As suggested by @derHugo you can add a null check here
// which will prevent initializing these parameters twice
// see his comment for more info
if (UnitySynchronizationContext == null) {
UnitySynchronizationContext = SynchronizationContext.Current;
}
if (UnityThreadId == null) {
UnityThreadId = Thread.CurrentThread.ManagedThreadId;
}
}
public static int UnityThreadId
{
get; private set;
}
public static SynchronizationContext UnitySynchronizationContext
{
get; private set;
}
}
}
Run Code Online (Sandbox Code Playgroud)
我已经针对在编辑器模式下使用 Async/Await 进行了测试,因此请运行您自己的测试以确保此更改也适用于您的情况。
| 归档时间: |
|
| 查看次数: |
1030 次 |
| 最近记录: |