I'm trying to recreate a SpeedoMeter in unity where the RPM data is getting sent from a python Script. I used sockets to send the data to my unity app instead of request as for some reason, I keep getting an 'Invalid host - 404' Error. The data is being received by Unity but the GUI is not updating. I've declared the Image in the inspector but it still didn't update.
using UnityEngine;
using UnityEngine.UI;
using System.Net;
using System.Text;
using System.Threading;
using System.Net.Sockets;
public class Speedometer : MonoBehaviour
{
Thread thread;
public int connectionPort = 25001;
TcpListener server;
TcpClient client;
bool running;
public Image SpeedoMeter_Needle;
float currentSpeed = 0.0f;
volatile float targetSpeed = 0;
float needleSpeed = 100.0f;
void Start()
{
if (SpeedoMeter_Needle == null)
{
Debug.LogError("Speedometer Needle Image is not assigned!");
return;
}
thread = new Thread(GetData);
thread.Start();
}
void GetData()
{
server = new TcpListener(IPAddress.Any, connectionPort);
server.Start();
running = true;
while (running)
{
client = server.AcceptTcpClient();
NetworkStream nwStream = client.GetStream();
byte[] buffer = new byte[client.ReceiveBufferSize];
while (client.Connected)
{
int bytesRead = nwStream.Read(buffer, 0, client.ReceiveBufferSize);
if (bytesRead > 0)
{
string dataReceived = Encoding.UTF8.GetString(buffer, 0, bytesRead);
float parsedValue = ParseData(dataReceived);
UnityMainThreadDispatcher.Instance().Enqueue(() => targetSpeed = parsedValue);
nwStream.Write(buffer, 0, bytesRead);
}
}
client.Close();
}
server.Stop();
}
private static float ParseData(string dataString)
{
Debug.Log(dataString);
if (dataString.StartsWith("(") && dataString.EndsWith(")"))
{
dataString = dataString.Substring(1, dataString.Length - 2);
}
return float.Parse(dataString);
}
void Update()
{
if (targetSpeed != currentSpeed)
{
UpdateSpeed();
}
}
void UpdateSpeed()
{
if (targetSpeed > currentSpeed)
{
currentSpeed += Time.deltaTime * needleSpeed;
currentSpeed = Mathf.Clamp(currentSpeed, 0.0f, targetSpeed);
}
else if (targetSpeed < currentSpeed)
{
currentSpeed -= Time.deltaTime * needleSpeed;
currentSpeed = Mathf.Clamp(currentSpeed, targetSpeed, 200.0f);
}
SetNeedle();
}
void SetNeedle()
{
float rotationAngle = (currentSpeed / 200.0f * 240.0f - 120.0f) * -1.0f;
SpeedoMeter_Needle.rectTransform.localRotation = Quaternion.Euler(0, 0, rotationAngle);
}
}
import socket
import keyboard
import time
host, port = "127.0.0.1", 25001
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
def key_test():
rpm_init = 0.0
try:
while True:
if keyboard.is_pressed('q'):
rpm_init += 0.1
rpm_init = min(rpm_init, 140) # Cap at 140
else:
rpm_init -= 0.005
rpm_init = max(rpm_init, 0) # Prevent going below 0
message = str(rpm_init).encode("utf-8")
sock.sendall(message)
response = sock.recv(1024).decode("utf-8")
print(response)
time.sleep(0.05)
except KeyboardInterrupt:
print("Closing connection.")
sock.close()
key_test()
I want it to update my GUI according to the data sent from the python script. Any help is appreciated.
I'm trying to recreate a SpeedoMeter in unity where the RPM data is getting sent from a python Script. I used sockets to send the data to my unity app instead of request as for some reason, I keep getting an 'Invalid host - 404' Error. The data is being received by Unity but the GUI is not updating. I've declared the Image in the inspector but it still didn't update.
using UnityEngine;
using UnityEngine.UI;
using System.Net;
using System.Text;
using System.Threading;
using System.Net.Sockets;
public class Speedometer : MonoBehaviour
{
Thread thread;
public int connectionPort = 25001;
TcpListener server;
TcpClient client;
bool running;
public Image SpeedoMeter_Needle;
float currentSpeed = 0.0f;
volatile float targetSpeed = 0;
float needleSpeed = 100.0f;
void Start()
{
if (SpeedoMeter_Needle == null)
{
Debug.LogError("Speedometer Needle Image is not assigned!");
return;
}
thread = new Thread(GetData);
thread.Start();
}
void GetData()
{
server = new TcpListener(IPAddress.Any, connectionPort);
server.Start();
running = true;
while (running)
{
client = server.AcceptTcpClient();
NetworkStream nwStream = client.GetStream();
byte[] buffer = new byte[client.ReceiveBufferSize];
while (client.Connected)
{
int bytesRead = nwStream.Read(buffer, 0, client.ReceiveBufferSize);
if (bytesRead > 0)
{
string dataReceived = Encoding.UTF8.GetString(buffer, 0, bytesRead);
float parsedValue = ParseData(dataReceived);
UnityMainThreadDispatcher.Instance().Enqueue(() => targetSpeed = parsedValue);
nwStream.Write(buffer, 0, bytesRead);
}
}
client.Close();
}
server.Stop();
}
private static float ParseData(string dataString)
{
Debug.Log(dataString);
if (dataString.StartsWith("(") && dataString.EndsWith(")"))
{
dataString = dataString.Substring(1, dataString.Length - 2);
}
return float.Parse(dataString);
}
void Update()
{
if (targetSpeed != currentSpeed)
{
UpdateSpeed();
}
}
void UpdateSpeed()
{
if (targetSpeed > currentSpeed)
{
currentSpeed += Time.deltaTime * needleSpeed;
currentSpeed = Mathf.Clamp(currentSpeed, 0.0f, targetSpeed);
}
else if (targetSpeed < currentSpeed)
{
currentSpeed -= Time.deltaTime * needleSpeed;
currentSpeed = Mathf.Clamp(currentSpeed, targetSpeed, 200.0f);
}
SetNeedle();
}
void SetNeedle()
{
float rotationAngle = (currentSpeed / 200.0f * 240.0f - 120.0f) * -1.0f;
SpeedoMeter_Needle.rectTransform.localRotation = Quaternion.Euler(0, 0, rotationAngle);
}
}
import socket
import keyboard
import time
host, port = "127.0.0.1", 25001
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
def key_test():
rpm_init = 0.0
try:
while True:
if keyboard.is_pressed('q'):
rpm_init += 0.1
rpm_init = min(rpm_init, 140) # Cap at 140
else:
rpm_init -= 0.005
rpm_init = max(rpm_init, 0) # Prevent going below 0
message = str(rpm_init).encode("utf-8")
sock.sendall(message)
response = sock.recv(1024).decode("utf-8")
print(response)
time.sleep(0.05)
except KeyboardInterrupt:
print("Closing connection.")
sock.close()
key_test()
I want it to update my GUI according to the data sent from the python script. Any help is appreciated.
Share Improve this question asked 2 days ago Lord_KordecLord_Kordec 415 bronze badges 5 |1 Answer
Reset to default 0Your code has countless of potential issues. The most prominent one would be that you expect your chunks of data to be received in the same chunk fragmentation that you send them our. This is not necessarily the case. TCP is a streaming protocol and works on a two continuous streams, one for sending one for receiving data.
Potential problems I noticed in your code example:
- As mentioned in the comments, sending a single float value as a variable length string is not only a waste of bandwidth, but makes the receiving and parsing a lot more difficult.
- encoding and parsing numeric values to / from a string has many issues. The biggest one is culture settings. Not every culture uses a dot as decimal point and
float.Parse
by default uses the local culture. For inter-process or inter-system communication it's highly recommended to use the InvarientCulture for both, encoding and parsing. - Your parsing code on the C# side seems to expect the float string to be enclosed in parenthesis
( )
however your sending code does not seem to add them. Adding them wouldn't really help with the separation of individual values since your parsing still expects the incoming string to start and end with the parenthesis. - In Unity make sure your project has set Run In Background to true. Either in the PlayerSettings or from code by setting Application.runInBackground to
true
. Otherwise Unity will not update when it doesn't have focus.
As for solutions, there are several approaches.
Since you only send our single float values it would make sense to just send the binary 4 or 8 bytes that make up the float / double. In python you can use struct.pack to convert your double value into a binary float32 value. Though you have to be careful about the endianness (the ordering of the bytes). The format string struct.pack("<f", num)
would encode the number as 4 bytes in little-endian order while struct.pack(">f", num)
would encode it in big-endian order. On the C# side you could use the BinaryReader to read that float value by using the ReadSingle method. The BinaryReader
will expect little-endian order. A huge advantage of the BinaryReader
and ReadSingle
is that the method will block until it received 4 bytes. This should take care of fragmented data out of the box.
If you really want to stick to a text based solution you should define your own protocol and parse it accordingly. Text based protocols usually use some kind of delimiter or frame marker to tell the receiving side when a message is complete. Many text based protocols use "lines" as delimiter (either "\n" or "\r\n"), but technically you could use anything. Your parenthesis would actually work, but you have to do more bookkeeping on the receiving side. You usually would search for the start character (opening parenthesis) and from there look for the closing one (string.IndexOf). Though most importantly when you received more than one message or message fragment, you have to keep the rest which would be part of the next message. When a new string fragment arrives you would concat it with the remaining part. Otherwise you may loose messages when they are cut off in between.
There's of course also a hybrid approach. Protocols like websockets also introduce the concept of "frames". So you just send a small binary header that tells the receiving side how much data is following. That way you don't need a delimiter and you could send arbitrary data in clean "packets". Some time ago I made an example TCP server / client on Unity Answers. The server was a standalone .NET application that sends out png images with a single integer length as header. The client is a MonoBehaviour
in Unity. This was the most straight forward approach, though limited to only that purpose.
You have to design your own application level protocol and ensure that the receiver can make sense of the data that arrives in that stream.
Yet another solution would be to not use TCP as protocol but something like UDP. While TCP is usually transported over a packet based protocol (usually IP, the Internet Protocol), TCP itself represents a stream. So it requires extra work to cut that stream back into the proper chunks of data. UDP is a packet based protocol, but it's a connection-less protocol. It sends and receives actual packets. Though the two major disadvantages are that UDP is unreliable which means that packets can be lost without notice or arrive out of order (that's usually only an issue when routed through the internet). The second disadvantage is that the packets can't be too big. You usually want to stay around 512 bytes max to ensure you're not going over the MTU size The main advantages are that you receive separate packets and that the latency is low. TCP has to ensure that data arrives intact and in order. Packets can always be lost but TCP makes sure that data is resent when something is missing. This can add delay when the connection is bad. Most fast paced games use UDP for sending out things like the player position frequently. Lost packets are simply ignored in such cases.
I'm not that comfortable with python so I can't give you a working example.
UnityMainThreadDispatcher
in the scene? Otherwise nothing will be working off the things you enqueued into the main thread queue ... – derHugo Commented 2 days agofloat
value it would be enough to send4 byte
.. if instead of send the value as a string e.g."32.000001"
you suddenly need9 char
– derHugo Commented 2 days ago