SetActive() 只能从主线程调用

Ori*_*rif 11 c# unity-game-engine firebase

我被这个问题困了 3 天,我做了很多研究,但找不到任何答案,这是对正在发生的事情的简要说明,尝试使用 Firebase 数据库和 Unity3D 身份验证,以下是步骤:

第一个用户登录,如果成功,它将从数据库中获取用户的数据,然后应该停用身份验证面板并激活用户面板。

尝试 SetActive 面板时,它给了我这个错误。

SetActive 只能从主线程调用。加载场景时,将从加载线程中执行构造函数和字段初始值设定项。不要在构造函数或字段初始值设定项中使用此函数,而是将初始化代码移至 Awake 或 Start 函数。UnityEngine.GameObject:SetActive(Boolean)

public void SignInWithEmail()
{
    auth.SignInWithEmailAndPasswordAsync(email, password).ContinueWith(task => {
      DatabaseReference.GetValueAsync().ContinueWith(task => {

        //here after successful signing in, it gets the data from the Database
        //and after that it should activate the user panel
        //and deactivate the authentication panel

        //HERE IS THE PROBLEM 
        userPanel.SetActive(true);
        authPanel.SetActive(false);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我不是要加载另一个场景或其他任何东西。

如果需要,我可以提供更多信息

olo*_*nge 18

因此,我的答案与Milod 的公认答案非常相似,但略有不同,因为即使他仍然有效,我还是花了一段时间才将头环绕在他的身上。

  1. 问题: 通常,您的所有代码都在 Unity 中的单个线程上运行,因为 Unity 是单线程的,但是当使用需要回调的 Firebase 等 API 时,回调函数将由新线程处理。这可能会导致竞争条件,尤其是在像 Unity 这样的单线程引擎上。

  2. 解决方案(来自 Unity): 从 Unity 2017.X 开始,Unity 现在要求更改 UI 组件以在主线程(即使用 Unity 启动的第一个线程)上运行。

  3. 受到什么影响?: 主要是调用修改 UI 之类的...

    gameObject.SetActive(true);  // (or false)
    textObject.Text = "some string" // (from UnityEngine.UI)
    
    Run Code Online (Sandbox Code Playgroud)
  4. 这与您的代码有何关系:

gameObject.SetActive(true);  // (or false)
textObject.Text = "some string" // (from UnityEngine.UI)
Run Code Online (Sandbox Code Playgroud)
  1. 建议的解决方案: 对于那些需要回调函数的调用,比如...
DatabaseReference.GetValueAsync()
Run Code Online (Sandbox Code Playgroud)

...你可以...

  • 将它们发送到设置为在该初始线程上运行的函数。
  • ...并且使用队列来确保它们将按照添加的顺序运行。
  • ...并按照 Unity 团队建议的方式使用单例模式。

实际解决方案

  1. 将下面的代码放入您的场景中,该游戏对象将始终处于启用状态,以便您拥有一个工作人员...
    • 总是在本地线程上运行
    • 可以发送那些在本地线程上运行的回调函数。
public void SignInWithEmail() {
    // auth.SignInWithEmailAndPasswordAsyn() is run on the local thread, 
    // ...so no issues here
    auth.SignInWithEmailAndPasswordAsync(email, password).ContinueWith(task => {

     // .ContinueWith() is an asynchronous call 
     // ...to the lambda function defined within the  task=> { }
     // and most importantly, it will be run on a different thread, hence the issue
      DatabaseReference.GetValueAsync().ContinueWith(task => {

        //HERE IS THE PROBLEM 
        userPanel.SetActive(true);
        authPanel.SetActive(false);
    }
  }
}

Run Code Online (Sandbox Code Playgroud)
  1. 现在从您的代码中,您可以简单地调用...

     UnityMainThread.wkr.AddJob();
    
    Run Code Online (Sandbox Code Playgroud)

...以便您的代码易于阅读(和管理),如下所示...

DatabaseReference.GetValueAsync()
Run Code Online (Sandbox Code Playgroud)

  • 嗨,苏利亚。不应该有任何额外的开销。这里的问题是谁(或什么)来完成这项工作。内部值得信赖的人(或线索)(我们知道我们能够根据需要影响他们),而不是未经审查的“其他人”。..但需要完成的工作量保持不变。请原谅这个类比,希望它能帮助澄清问题。 (2认同)