由于线程原因,实例化不起作用
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;
}
}
我收到以下错误
"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;
}
}