实例化不适用于线程原因

时间:2015-08-11 12:34:15

标签: c# json multithreading unity3d instantiation

我收到以下错误

  

" INTERNAL_CALL_Internal_InstantiateSingle只能从主线程调用。"       "在加载场景时,将从加载线程执行构造函数和字段初始值设定项。"       "不要在构造函数或字段初始值设定项中使用此函数,而是将初始化代码移动到唤醒或启动函数。"

每当我尝试使用此函数实例化某些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);
}

使用按钮调用一次。

我似乎无法找到问题,但如上所述,错误说它是一个线程问题,并且我不能从主要的另一个线程调用实例化。我不知道如何将实例化更改为另一个线程,但是非常感谢任何帮助解决这个问题。

2 个答案:

答案 0 :(得分:0)

您可以尝试使用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();
    }
}

答案 1 :(得分:0)

来自我的SAME QUESTION ON THE GAME DEVELOPMENT EXCHANGE 答案trojanfro

查看WebClient docs,该方法描述为:

  

从资源下载string,而不会阻止调用线程。

并给出错误消息:

  

INTERNAL_CALL_Internal_InstantiateSingle只能从主线程中调用

很明显,您需要下载数据并存储它,并且只有在下载完成后才能在主线程中调用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;
    }
}