gpt4 book ai didi

c# - 字段初始化后无法从主线程访问对象

转载 作者:行者123 更新时间:2023-12-03 13:10:18 25 4
gpt4 key购买 nike

我们正在制作一个统一的游戏,该游戏使用在tcp套接字处接收到的命令来处理游戏在特定校准状态下的 Action 。
当接收到新的字符串时,状态管理器处理套接字管理器引发的事件。然后,该状态管理器必须在开始时已在字段中引用的游戏对象上触发一种方法。

我们现在面临的问题是,处理这些事件的线程无法访问该对象。我们收到以下错误:

ToString can only be called from the main thread. Constructors and field initializers will be executed from the loading thread when loading a scene.



在标有“此行​​表示错误”的行上

Unity如何使用EventHandlers处理线程,以及如何访问该对象?

提前致谢!
public class StateManager : MonoBehaviour {

private bool initialized;
private GameObject calibrationController;


void Start(){
InitializeEventHandler ();
}

void OnLevelWasLoaded(int level) {
if (level == 3) {
calibrationController = GameObject.FindGameObjectWithTag("CalibrationController");
Debug.Log ("calibrationController 1: " + calibrationController);
calibrationController.GetComponent<CalibrationController> ().NewState += NewCalibrationState;
calibrationController.GetComponent<CalibrationController>().setupCalibration();
}
}

private void InitializeEventHandler(){
GetComponent<GameSocket> ().NewCommand += NewCommandReceived;
}

private void NewCommandReceived(object sender, NewCommandEventArgs e){
HandleCommandReceived (e.Command);
}

private void NewCalibrationState(object sender, NewCalibrationStateEventArgs e)
{
HandleNewCalibrationState (e.State);
}

private void HandleCommandReceived(string command){
switch (command) {
case "startcalibrationcomplete":
Debug.Log ("startcalibrationcomplete");
Debug.Log ("calibrationController 2: " + calibrationController); THIS LINE GIVES THE ERROR !!!
Debug.Log(GameObject.FindGameObjectWithTag("CalibrationController"));
break;
default:
Debug.Log ("state10");
break;
}
}

private void HandleNewCalibrationState(string state){
switch (state) {
case "startcalibration":
GetComponent<GameSocket>().MySend("startcalibration");
// ...
break;
case "animationdone":
GetComponent<GameSocket>().MySend("animationdone");
break;
default:
Debug.Log ("state10");
break;
}
}

For future readers: It is impossible to call methods on gameobjects from the event handlers due to Unity's poor threading. I found a workaround by letting the event handlers set properties of a data script on an empty gameobject. The data on this script can be accessed from whichever gameobject i.e. in an update cycle.

最佳答案

事实是 Unity 是否不支持,不支持线程。几年前,当从另一个线程中调用它们时,他们将API更改为引发异​​常。

当您通过另一个线程从引发事件时,那个事件在该线程中被调用,而不在中被调用。 无法使用来自该事件回调的,具有线程 限制的Unity API。

我对Unity中的线程进行了许多实验,得出的结论是,以下是如何将
线程集成到Unity API中的集成的。

要在Unity中使用线程,您必须使用lock,这是一个boolean全局变量,可以从Unity 线程和创建的TCP线程访问。当网络接收到时,您还需要一种方法来调用 Unity API 。使用lockboolean variables应该可以完成。然后,您可以从更新 函数而不是新创建的线程调用 Unity API的

请勿主线程中创建TCP instance,然后尝试从另一个线程访问它。 在另一个线程中创建访问 TCP实例 。如果这样做,将导致崩溃/冻结或错误。

下面的代码应该向您展示如何在Unity中使用TCP和线程引发事件。下面代码的目标主线程(更新功能)而不是另一个线程的引发事件。您可以根据需要向此代码添加任意多个事件。

public class TCPRECEIVER: MonoBehaviour
{

readonly object locker = new object(); //For Locking variables
bool continueReading = false;
bool gotNewMessage = false;

byte[] receivedBytes; //Stores bytes received from the server (will be accessed from Multiple Threads with lock)


//Event to notify other functions when something is received
public delegate void newMessageReceieved(byte[] bytesFromServer);
public static event newMessageReceieved onNewMessageReceieved;


public void Start()
{
receivedBytes = new byte[40];

}


void Update()
{
//Lock is expensive so make sure that we are still in reading mode before locking
if (continueReading)
{
//lock variables
lock (locker)
{
//Check if there is a new message
if (gotNewMessage)
{
gotNewMessage = false; //Set to false so that we don't run this again until we receive from server again

//Raise the event here if there are subscribers
if (onNewMessageReceieved != null)
{
onNewMessageReceieved(receivedBytes);
}
}
}
}
}

//Start Reading from Server
void startReading()
{
continueReading = true;

//Start new Thread
new System.Threading.Thread(() =>
{
//Create Client outside the loo
System.Net.Sockets.TcpClient tcpClient = new System.Net.Sockets.TcpClient("192.168.1.1", 8090);
System.Net.Sockets.NetworkStream tcpStream = tcpClient.GetStream();

//Read Forever until stopReading is called
while (continueReading)
{
byte[] bytesToRead = new byte[tcpClient.ReceiveBufferSize];
int bytesRead = tcpStream.Read(bytesToRead, 0, tcpClient.ReceiveBufferSize);

//Check if we received anything from server
if (bytesRead > 0)
{
//lock variables
lock (locker)
{
//Copy the recived data to the Global variable "receivedBytes"
System.Buffer.BlockCopy(bytesToRead, 0, receivedBytes, 0, bytesRead);

//Notify the Update function that we got something
gotNewMessage = true;
}
}
System.Threading.Thread.Sleep(1); //So that we don't lock up
}
}).Start();
}

//Stop reading
void stopReading()
{
continueReading = false;
}
}

然后,您可以使用以下方法从其他类别订阅和取消订阅事件/消息:
public void OnEnable()
{
//Subscribe to the event
TCPRECEIVER.onNewMessageReceieved += receivedBytesFromServer;
}

public void OnDisable()
{
//Un-Subscribe to the event
TCPRECEIVER.onNewMessageReceieved -= receivedBytesFromServer;
}


void receivedBytesFromServer(byte []bytesFromServer)
{
//Do something with the bytes
}

关于c# - 字段初始化后无法从主线程访问对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36206132/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com