0
votes

I have an application within Unity3D (acting as a server) that receives messages from an exterior application (single client) with the following structure:

number(float) number(float) number(float)

The first two numbers represent the local position (x,z axis) and the last one a rotation value (y axis).

The goal is to use this data to update the Camera gameobject position (using the LoadPositions method) within the game scene. From what I've read manipulating gameobjects while outside Unity's main thread is not possible.

With that being said how can I change from and to Unity main thread so that I can both listen for messages and update the gameobjects position. Also, anyone happens to know of a working example of a simple TCP Server in Unity without having to resort to threads?

using UnityEngine;
using System.Collections;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System;
using System.Text;
using System.Collections.Generic;

public class ProxyThreadServer : MonoBehaviour {

float x; 
float z; 
float rot;

Vector3 updatePos;
Vector3 updateRot;

string ip_address  = "127.0.0.1";
string msgReceived;
string[] words;

int wordsNum; 
int port = 8000;
int numSurf;
int jumpInterval;

Thread listen_thread;
TcpListener tcp_listener;
Thread clientThread;
TcpClient tcp_client;
bool isTrue = true;

// Use this for initialization
void Start () 
{
    IPAddress ip_addy = IPAddress.Parse(ip_address);
    tcp_listener = new TcpListener(ip_addy, port);
    listen_thread = new Thread(new ThreadStart(ListenForClients));
    listen_thread.Start();
}

private void ListenForClients()
{
    this.tcp_listener.Start();

    while(isTrue == true)   
    {
        //blocks until a client has connected to the server
        TcpClient client = this.tcp_listener.AcceptTcpClient();

        //create a thread to handle communication 
        //with connected client
        clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
        clientThread.Start(client);

        Debug.Log("Got client " + client);

    }
}

private void HandleClientComm(object client)
{
    tcp_client = (TcpClient)client;
    NetworkStream client_stream = tcp_client.GetStream();

    byte[] message = new byte[4096];
    int bytes_read;

    while(isTrue == true)
    {
        bytes_read = 0;

        try
        {
            //blocks until a client sends a message
            bytes_read = client_stream.Read(message, 0, 4096);
            //Debug.Log(message);

        }
        catch (Exception e)
        {
            //a socket error has occurred
            Debug.Log(e.Message);
            break;
        }

        if(bytes_read == 0)
        {
            //client has disconnected
            Debug.Log("Disconnected");
            tcp_client.Close();
            break;
        }

        ASCIIEncoding encoder = new ASCIIEncoding();
        Debug.Log(encoder.GetString(message,0,bytes_read));

        msgReceived = encoder.GetString(message,0,bytes_read);
        LoadPositions(msgReceived);
    }

    if(isTrue == false)
    {
        tcp_client.Close();
        Debug.Log("closing tcp client");
    }

}

void OnApplicationQuit()
{
    try
    {
        tcp_client.Close();
        isTrue = false;
    }
    catch(Exception e)
    {
        Debug.Log(e.Message);
    }

    // You must close the tcp listener
    try
    {
        tcp_listener.Stop();
        isTrue = false;
    }
    catch(Exception e)
    {
        Debug.Log(e.Message);
    }
}


 void LoadPositions(string positions){

    // Split string on spaces. This will separate all the words.
    words = positions.Split(' ');
    wordsNum = words.Length;

    for (int i = 0; i <= wordsNum; i++) {

        x = float.Parse(words[0], System.Globalization.CultureInfo.InvariantCulture); 
        z = float.Parse(words[1], System.Globalization.CultureInfo.InvariantCulture); 
        rot = float.Parse(words[2], System.Globalization.CultureInfo.InvariantCulture);

        Debug.Log("Reading position: " + "x: " + x + " z: " + z + " yRot: " + rot);

        updatePos = new Vector3(x, this.gameObject.transform.position.y, z);
        this.gameObject.transform.position = updatePos;

        updateRot = new Vector3(this.gameObject.transform.rotation.x, rot / 4, this.gameObject.transform.rotation.z);
        this.transform.localEulerAngles = updateRot;

        //UpdateCameraMatrix();
        //StartCoroutine(UpdateSurfs());
        }
   }

}
2

2 Answers

1
votes

While I haven't tried to do something quite like this before, assuming the limitations do exist as you mentioned, my approach would be to use a queue to store the messages then process them in the order they came in on the unity thread. So instead of calling LoadPositions when it comes it, add it to a queue

pendingMessages.Enqueue(msgReceived);

Then in the update method you process it:

void Update()
{
    while (pendingMessages.Count() > 0)
        LoadPositions(pendingMessages.Dequeue());
}
0
votes

You can use .NET's Async TCP. It's based on callback delegates. (Working with it is a bit tricky though)