我在这个问题上呆了三天,我做了很多研究,但找不到任何答案,这是正在发生的事情的简要说明,尝试使用Firebase数据库和Unity3D进行身份验证,在这里步骤如下:
第一个用户登录,如果登录成功,它将从数据库中获取用户的数据,然后应禁用“身份验证”面板并激活“用户”面板。
尝试设置Active面板时,出现此错误。
SetActive只能从主线程调用。 加载场景时,将从加载线程执行构造函数和字段初始化程序。 不要在构造函数或字段初始化程序中使用此函数,而应将初始化代码移至Awake或Start函数。 UnityEngine.GameObject:SetActive(布尔)
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);
}
}
}
我不是要加载其他场景或其他任何内容。
答案 0 :(得分:1)
因此基本上UI元素需要在Main线程中进行修改,我发现此脚本可以在Main线程中执行您的函数,只需将您的函数放入协程和入队< / strong>到脚本(UnityMainThreadDispatcher)。 (您需要在场景中添加一个对象,然后向其中添加MainThreadDispathcer脚本)
这是我的函数的外观:
byte[] imageData = Files.readAllBytes(Paths.get("testImage4.jpg"));
这是在Main Thead中运行它的脚本
public void SignInWithEmail()
{
auth.SignInWithEmailAndPasswordAsync(email, password).ContinueWith(task => {
DatabaseReference.GetValueAsync().ContinueWith(task => {
//Here's the fix
UnityMainThreadDispatcher.Instance().Enqueue(ShowUserPanel());
}
}
}
public IEnumerator ShowUserPanel()
{
uiController.userPanel.panel.SetActive(true);
uiController.authPanel.SetActive(false);
yield return null;
}
答案 1 :(得分:1)
所以我的答案与 Milod's 所接受的答案非常相似,但是有些不同,尽管我花了一些时间将自己的头缠在他的身上,尽管他仍然在工作。
问题: 通常,您的所有代码都在Unity中的单个线程上运行,因为Unity是单线程的, 但是,当使用Firebase等需要回调的API时,回调函数将由新线程处理。 这可能会导致竞争条件,尤其是在像Unity这样的单线程引擎上。
解决方案(来自Unity): 从Unity 2017.X开始,Unity现在需要对UI组件进行更改才能在Main线程(即从Unity启动的第一个线程)上运行。
受影响的是什么?: 主要是修改UI的调用,例如...
gameObject.SetActive(true); // (or false) textObject.Text = "some string" // (from UnityEngine.UI)
这与您的代码有何关系:
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);
}
}
}
DatabaseReference.GetValueAsync()
...你可以...
using System;
using System.Collections.Generic;
using UnityEngine;
internal class UnityMainThread : MonoBehaviour
{
internal static UnityMainThread wkr;
Queue<Action> jobs = new Queue<Action>();
void Awake() {
wkr = this;
}
void Update() {
while (jobs.Count > 0)
jobs.Dequeue().Invoke();
}
internal void AddJob(Action newJob) {
jobs.Enqueue(newJob);
}
}
现在从您的代码中,您只需调用...
UnityMainThread.wkr.AddJob();
...以便您的代码保持易于阅读(和管理),如下所示...
public void SignInWithEmail() {
auth.SignInWithEmailAndPasswordAsync(email, password).ContinueWith(task => {
DatabaseReference.GetValueAsync().ContinueWith(task => {
UnityMainThread.wkr.AddJob(() => {
// Will run on main thread, hence issue is solved
userPanel.SetActive(true);
authPanel.SetActive(false);
})
}
}
}
答案 2 :(得分:0)
请注意,Firebase 现在有一个很好的 ContinueWithOnMainThread
扩展方法,它比其他建议的答案更优雅地解决了这个问题:
using Firebase.Extensions;
public void SignInWithEmail() {
// This code runs on the caller's thread.
auth.SignInWithEmailAndPasswordAsync(email, password).ContinueWith(task => {
// This code runs on an arbitrary thread.
DatabaseReference.GetValueAsync().ContinueWithOnMainThread(task => {
// This code runs on the Main thread. No problem.
userPanel.SetActive(true);
authPanel.SetActive(false);
}
}
}```