Unity 解决 “... can only be called from the main thread” 问题

背景

有些属性或方法只能在主线程中调用,如 .gameObjectInstantiate() 等。这是 Unity 设计时的一个缺陷(为了保证线程安全),多年来一直没有修正,或者说官方懒得弄。

Instantiate() 为例,在非主线程调用时,报错大概如下所示。其他属性或方法的报错也大体相同。

UnityEngine.UnityException: Internal_CloneSingle can only be called from the main thread.
Constructors and field initializers will be executed from the loading thread when loading a scene.
Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.
  at (wrapper managed-to-native) UnityEngine.Object.Internal_CloneSingle(UnityEngine.Object)
  at UnityEngine.Object.Instantiate[T] (T original) [0x00012] in <25586dd8e41845059c446f7ba2bf37e4>:0 
  at ... (...) [0x0000a] in ... .cs:... 
UnityEngine.Debug:Log (object)

注:应注意,此问题在有些情况下 Unity 控制台不会提示任何错误,只是代码会在那里中断,这时可能会让人非常摸不着头脑。使用 try { } catch (Exception e) { Debug.Log(e); } 才能在控制台中显示出错误,以便验证是不是本文所述问题。

解决方法

使用第三方包:https://github.com/PimDeWitte/UnityMainThreadDispatcher.git

步骤 1

导入包:

  • 在 Unity 中,Window -> Package Manager -> 加号 -> Add package from git URL,输入上面的仓库地址,Add。

或者:

  • 修改 Packages\manifest.json,在 dependencies 中加入一行:
"dependencies": {
    
    
  "com.pimdewitte.unitymainthreaddispatcher": "https://github.com/PimDeWitte/UnityMainThreadDispatcher.git",
}

步骤 2

在场景内创建一个空物体,挂载 UnityMainThreadDispatcher 组件。

步骤 3

在你的代码中,按照下面的方法调用原来出问题的语句,问题解决。

普通:

UnityMainThreadDispatcher.Instance().Enqueue(() =>
{
    
    
    // 只能在主线程执行的语句
    // ...
});

协程:

private IEnumerator Do()
{
    
    
    // 只能在主线程执行的语句
    // ...
    yield return null;
}

private void Fun()
{
    
    
    UnityMainThreadDispatcher.Instance().Enqueue(Do());
}

参考资料

  1. https://forum.unity.com/threads/working-with-threads-can-only-be-called-from-the-main-thread.513899/#post-7779918
  2. https://github.com/PimDeWitte/UnityMainThreadDispatcher

猜你喜欢

转载自blog.csdn.net/xzqsr2011/article/details/128693150