Unity as a simulation environment - how to delay the Update() loop while big calculation is performed

Dear community,

I am quite new to Unity and i am currently using it as a tool for designing a simple simulated environment for my pilot research study.
Here is a simplified explanation:

  1. The main character (agent) scans the environment and sends the state (as a JSON object) via TCP socket to a python server.
  2. On the python server side a big calculation is performed based on the the state. The result is an action that is being sent back via TCP to Unity.
  3. The character needs to perform this action in Unity and then again scan the environment. The new state is sent again to the python server…
  4. This process is being repeated 'till infinity (unless the client or server is stopped) eventually leading to the agent self-developing a behavior based on its learning.

I finished creating the environment in unity as well as programmed the learning algorithm in python. Moreover, i managed to establish TCP connection between the two. However, i stumbled across the problem of the main Update() loop in Unity.

Namely, if i simplify the process to Unity sending ping (being the state) and python sending pong (being the action) i need the following process to be repeated.

  1. freeze simulation
  2. ping
  3. calculation
  4. pong
  5. unfreeze simulation

So in the Unity Start()method i setup the socket and send the initial ping. What i would like in the main Update() loop is to make Unity wait for python’s pong answer before updating the frame. I created a script containing a toy example and tried to simulate the calculation-time in python by adding 2 seconds of delay before sending the pong to Unity.

Here is the code for the Unity script (just attach it to the Main Camera):

using UnityEngine;
using System;
using System.IO;
using System.Net.Sockets;

public class networkSocketPingPong : MonoBehaviour
{
    public String host = "localhost";
    public Int32 port = 50000;

    internal Boolean socket_ready = false;
    internal String input_buffer = "";

    TcpClient tcp_socket;
    NetworkStream net_stream;

    StreamWriter socket_writer;
    StreamReader socket_reader;

    private void Start()
    {
        setupSocket();
        writeSocket("ping");
    }

    
     void Update()
     {
        
        string received_data = readSocket();
        
        switch (received_data)
             {
                 case "pong":
                     Debug.Log("Python controller sent: " + (string)received_data);
                     writeSocket("ping");
                     break;
                 default:
                     Debug.Log("Nothing received from Python");
                     break;
         }
    }

    void OnApplicationQuit()
    {
        closeSocket();
    }

    // Helper methods for:
    //...setting up the communication
    public void setupSocket()
    {
        try
        {
            tcp_socket = new TcpClient(host, port);
            net_stream = tcp_socket.GetStream();
            socket_writer = new StreamWriter(net_stream);
            socket_reader = new StreamReader(net_stream);
            socket_ready = true;
        }
        catch (Exception e)
        {
            // Something went wrong
            Debug.Log("Socket error: " + e);
        }
    }

    //... writing to a socket...
    public void writeSocket(string line)
    {
        if (!socket_ready)
            return;

        socket_writer.Write(line);
        socket_writer.Flush();
    }

    //... reading from a socket...
    public String readSocket()
    {
       if (!socket_ready)
        {
            Debug.Log("Socket is not ready");
            return "";
        }

        if (net_stream.DataAvailable)
            return socket_reader.ReadLine();

        return "";
    }

    //... closing a socket...
    public void closeSocket()
    {
        if (!socket_ready)
            return;

        socket_writer.Close();
        socket_reader.Close();
        tcp_socket.Close();
        socket_ready = false;
    }
}

…and here is the python server code:

import socket
import time

host = 'localhost' 
port = 50000
backlog = 5 
size = 1024 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.bind((host,port)) 
s.listen(backlog) 

while 1:
    client, address = s.accept() 
    print "Client connected."
    while 1:
        data = client.recv(size)
        if data == "ping":
            time.sleep(2)
            print ("Unity Sent: " + str(data))
            client.send("pong

")
else:
client.send(“Bye!”)
print ("Unity Sent Something Else: " + str(data))
client.close()
break

I first run the python server and then the simulation (via Unity editor). This results with the following console screenshot:

This proves that the main Update() loop is running while i want it to pause and update only when “pong” is received from the server. Any ideas how to achieve this?

I searched the forum for answers but always stumbled upon questions that ask for the opposite - how to make Unity not to freeze - and answers that suggest using Coroutines or Threads.

Any help will be much appreciated.

Thanks in advance!!!

As I read through your problem, you want to freeze simulation in the client app until your server responds with the necessary action.

You can freeze the Unity app by setting Time.timescale = 0 and resume it when you recieve the response from server by setting timescale back to 1.

But I suggest you to use IEnumerators instead of Update function, since freezing the execution of default functions like Update and FixedUpdate isn’t a recommended way to do things.

What I recommend is you can define the task to perform (finding the status, or whatever your client app is supposed to do) in an IEnumerator function and yield a return once your server responds and finally start the coroutine recursively.