4

所以,我有这个用 Unity 编写的游戏,它应该通过 UDP 实时接收数据。数据将通过无线方式从一个用 Java 编写的 Android 设备传来,而 Unity 程序是用 C# 编写的。我的问题是,每当我尝试声明一个UdpClient对象,并在 UnityReceive()Update()方法中调用它的方法时,游戏就会挂起。这是我试图放入我的Update()方法中的代码 -

UdpClient client = new UdpClient(9877);
IPEndPoint receivePoint = new IPEndPoint(IPAddress.Parse("192.168.1.105"), 9877);
byte[] recData = client.Receive(ref receivePoint);

但这会导致游戏挂起。

然后我尝试了另一种方法 - 我尝试在单独的线程中接收数据。如果我所要做的就是接收 byte 数组,它就像魔术一样工作。没有问题。除了我还需要将接收到的数据用作实际游戏中使用的函数的参数(现在,假设我需要在主游戏窗口中将接收到的数据字节显示为字符串)。但是,我不知道 Unity 中的跨线程是如何工作的。我试过这个 -

string data = string.Empty;
private IPEndPoint receivePoint;

void OnGUI()
{
    GUI.Box(new Rect(20, 20, 100, 40), "");
    GUI.Label(new Rect(30, 30, 100, 40), data);        
}

void Start()
{
    LoadClient();
}

public void LoadClient()
{
    client = new UdpClient(9877);
    receivePoint = new IPEndPoint(IPAddress.Parse("192.168.1.105"), 9877);
    Thread startClient = new Thread(new ThreadStart(StartClient));
    startClient.Start();
}

public void StartClient()
{
    try
    {
        while (true)
        {
            byte[] recData = client.Receive(ref receivePoint);

            System.Text.ASCIIEncoding encode = new System.Text.ASCIIEncoding();
            data = encode.GetString(recData);
        }
    }
    catch { }
}

但是如果我尝试上述方法,我的程序就会挂起。那么,我到底错过了什么?

4

1 回答 1

2

它为您挂起的原因是因为这Receive是定义的方式。它会阻止您当前的代码,直到网络连接(即底层套接字)上有可用数据。您应该为此使用后台线程是正确的。

但请注意,在游戏对象脚本中创建线程可能会很危险,例如,如果您同时将脚本附加到多个对象。您不希望同时运行此脚本的多个版本,因为它们都会尝试绑定到同一个套接字地址(这不起作用)。如果游戏对象死亡,您还需要注意关闭线程(这在 C# 中不会自动完成 -必须停止线程)。

也就是说,当您使用多个线程时,您需要确保线程安全。这意味着您需要保护数据,以便在写入时无法读取它。最简单的方法是使用C# 锁

private readonly Object _dataLock = new Object();
private string _sharedData = String.Empty;

void OnGUI()
{
    string text = "";
    lock (_dataLock)
        text = _sharedData;
}

void StartClient()
{
    // ... [snip]
    var data = Encoding.ASCII.GetString(recData);
    lock (_dataLock)
        _sharedData = data;
}

请注意,锁可能会稍微损害性能,尤其是在频繁使用的情况下。在 c# 中还有其他方法可以保护数据,这些方法性能更高(但稍微复杂一些)。有关一些示例,请参阅Microsoft 的此指南

于 2012-05-30T07:09:18.100 回答