由于线程原因,实例化不起作用

Instantiation not working for a threading reason

我收到以下错误

"INTERNAL_CALL_Internal_InstantiateSingle 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."

每当我尝试使用此函数实例化某些 GameObject's 时:

public void DisplayCalls () {
    for (int i = 0; i < calls [0].Count; i++) {
        for (int j = 0; j < impFields.Length; j++) {
            GameObject textClone = Instantiate (textBox, Vector3.zero, Quaternion.Euler(Vector3.zero)) as GameObject;
            textClone.transform.SetParent (canvas.transform);
            Text text = textClone.GetComponent <Text> ();
            text.text = calls[impFields [j]][i];
            text.alignment = TextAnchor.MiddleCenter;
            textClone.transform.localPosition = new Vector3 (-262.5f + (175f * j), (182f + (30f * i)), 0f);
        }
    }
}

该函数是使用此其他函数调用的:

public void GetPanelInfo () {
    string file = "getpanelinfo.php";
    string hash = Md5Sum (mostRecentID.ToString () + SecretKey1 + SecretKey2);
    string parameters = "?mostrecentid=" + mostRecentID.ToString () + "&hash=" + hash;
    string fullURL = baseURL + file + parameters;
    Uri fullURI = new Uri (fullURL);
    WebClient client = new WebClient ();

    string jsonString = string.Empty;
    client.DownloadStringCompleted += (sender, e) => {
        if (!e.Cancelled && e.Error == null) {
            jsonString = e.Result;
            JSONNode json = JSON.Parse (jsonString);
            for (int i = 0; i < json["calls"].Count; i++) {
                for (int j = 0; j < calls.Length; j++) {
                    calls[j].Add (json["calls"][i][names [j]]);
                }
                mostRecentID = json["calls"][i]["callID"].AsInt;
            }
        } else if (e.Cancelled && e.Error == null) {
            Debug.Log ("Cancelled");
        } else {
            Debug.Log ("Error: " + e.Error);
        }
        loading = false;
        LogCalls ();
        DisplayCalls ();

    };
    client.DownloadStringAsync (fullURI);
}

使用按钮调用一次。

我似乎找不到问题,但如上所述,错误表明这是一个线程问题,我无法从主线程以外的另一个线程调用实例化。我不知道如何将实例化更改为另一个线程,但如果能帮助解决这个问题,我将不胜感激。

您可以尝试使用UnityEvent通知主线程事件。代码将类似于以下内容:

using UnityEngine.Events;

public class YourClass : MonoBehaviour
{
    private UnityEvent m_MyEvent = new UnityEvent();

    void Start()
    {
        m_MyEvent.AddListener(DisplayCalls);
    }

    public void GetPanelInfo () {
        ...
        // instead of this : 
        // DisplayCalls (); 
        // do this:
        m_MyEvent.Invoke();
    }
}

取自我的 SAME QUESTION ON THE GAME DEVELOPMENT EXCHANGE trojanfro.

的回答

查看WebClient docs,方法描述为:

Downloads a string from a resource, without blocking the calling thread.

并给出错误信息:

INTERNAL_CALL_Internal_InstantiateSingle can only be called from the main thread

很明显,您需要下载数据并存储它,并且只有在下载完成后才在主线程中调用DisplayCells。这不像在Cocoa中那样唯一,例如,您只能在主线程上调用与UI相关的API方法。

我花了一些时间在 Google 上寻找任何关于这方面的有用信息(我是 Unity/C# 的初学者),但我 认为 这样就可以了:

创建一个布尔值来显示有数据要显示:

private bool _callsToDisplay = false;

设置数据下载完成后:

client.DownloadStringCompleted += (sender, e) => {
    ...
    loading = false;
    LogCalls ();
    _callsToDisplay = true;
};

然后在 Update() 方法(在主线程上运行)中,如果设置了布尔值,则调用 DisplayCalls()

void Update()
{
    if (_callsToDisplay) {
        DisplayCalls();
        _callsToDisplay = false;
    }
}