3
\$\begingroup\$

I receive the following error

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

whenever I try to instantiate some GameObject's using this function:

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);
        }
    }
}

The function is called using this other function:

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);
}

Which is called once using a button.

I cannot seem to find the problem, but as stated above, the error says that it is a threading problem and that I cannot call instantiate from another thread other than the main one. I do not know how to change the instantiation to another thread, but any help to solve this problem would be much appreciated.

\$\endgroup\$
2
\$\begingroup\$

Looking at the WebClient docs, the method is described as:

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

and given the error message:

INTERNAL_CALL_Internal_InstantiateSingle can only be called from the main thread

It's clear that you need to download the data and store it, and only call DisplayCells, in the main thread, once the download is complete. This is not unique as in Cocoa, for example, you can only call UI-related API methods on the main thread.

It took me some hunting on Google to find any decent info on this (I am a beginner when it comes to Unity/C#), but I think this will do it:

Create a boolean that is shows there is data to display:

private bool _callsToDisplay = false;

Set that when the data has downloaded:

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

and then in the Update() method (which runs on the main thread), call DisplayCalls() if that boolean is set:

void Update()
{
    if (_callsToDisplay) {
        DisplayCalls();
        _callsToDisplay = false;
    }
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks for the reply, but won't having the DisplayCalls() in the update function cause it to be called every frame? And such, won't it instantiate so many of my textBox's? One more request, can you elaborate on the 2 notes you stated? A newbie myself, I don't really understand the concepts of storing data in objects too much. Thanks! \$\endgroup\$ – Cold999 Aug 12 '15 at 12:58
  • \$\begingroup\$ It will only call DisplayCalls() if there is anything in the queue. I have thought of a simpler approach (editing answer). \$\endgroup\$ – trojanfoe Aug 12 '15 at 12:59
  • \$\begingroup\$ Works like a charm, thank you so much! As for the side notes you had before editing, do you think they would still be an improvement or is my code good enough as is? Thanks again. \$\endgroup\$ – Cold999 Aug 12 '15 at 13:05
  • \$\begingroup\$ You only need to package up the data in their own class if you are passing it around different GameObjects / Components. If the data is only used in this script, then what you have is fine. \$\endgroup\$ – trojanfoe Aug 12 '15 at 13:06
-1
\$\begingroup\$

I think this is happening because you are trying to Instantiate() in a class that does not derive from MonoBehaviour.

Ensure that the class you use to "Instantiate" gameobjects, derive from the MonoBehaviour base class, otherwise, you will be getting this error.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Unfortunately that isn't the source of the problem. My public void DisplayCalls is located in my public class Manager : MonoBehaviour. So it is under a MonoBehaviour class. \$\endgroup\$ – Cold999 Aug 12 '15 at 12:23
  • \$\begingroup\$ Why have you downvoted my answer? \$\endgroup\$ – trojanfoe Aug 12 '15 at 13:26
  • \$\begingroup\$ @trojanfoe Eh I didn't, but it says I did. And I can't undo unless you edit the question. \$\endgroup\$ – jgallant Aug 12 '15 at 13:35
  • \$\begingroup\$ @trojanfoe There are no down votes on your answer; additionally, there is no way to actually see which down votes came from which user and one is free to use one's votes however one wishes. Short of serial voting, which is not in evidence here, there is nothing appropriate for any moderator to look into here (nor would there be anything we could do about it in any case). Please do not use moderator flags for this purpose in the future, especially since this specific case appears to simply have been a legitimate accidental click. \$\endgroup\$ – user1430 Aug 12 '15 at 14:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.