Performance improvement/problem

Hi everyone,

Currently I’m working on a road and traffic simulation, which generates roads and random cars. The positions of the cars are update through an updating xml file through a websocket by another API in variable time intervalls. The problem is that the car positions are only given in 2D coordinates but the roads or its segments are generate in a 3D world so roads could have different heigths and the cars should be placed on the top of it. So for now I have create a coroutine, which reads the xml file ones per second and updates the next position of a car. During an car’s update the car interpolates its position and rotation to its next position until a new update is received. To set on new next position I’m using Raycasts starting on the 2D car position with a large high on which no roads could be generated, but the performance is very laggy in the simulation. Is there maybe a way to use threads or nested coroutines which solve the problem? I have noticed that I can’t use Raycasts in threads. The following code defines the position updating script attached to cars. The SetPositionAndRotation(…) methode is called outside inside the previous described coroutine.

using UnityEngine;
public class MoveCar : MonoBehaviour
{
Vector3 position1 = Vector3.zero;
Vector3 position2 = Vector3.zero;

Vector3 rotation1 = Vector3.zero;
Vector3 rotation2 = Vector3.zero;

int numberOfPositions = 0;
bool startLerp = false;
float distance = 0;
private float rotLimit;
float timer = 0.0f;

// Called after getting new Position from NodeManager
public void SetPositionAndRotation(Vector3 newPosition, float heading)
{
    switch (numberOfPositions)
    {
        case 0:
            RaycastHit rh;
            if(Physics.Raycast(newPosition + Vector3.up * 10000, Vector3.down, out rh, float.PositiveInfinity, LayerMask.GetMask(new string[] { "Road" })))
            {
                rotation1 = new Vector3(rh.collider.transform.eulerAngles.x, heading, rh.collider.transform.eulerAngles.z);
                position1 = new Vector3(rh.point.x, rh.point.y, rh.point.z);
            }

            numberOfPositions++;

            break;

        case 1:
            RaycastHit rh1;

            if(Physics.Raycast(newPosition + Vector3.up * 10000, Vector3.down, out rh1, float.PositiveInfinity, LayerMask.GetMask(new string[] { "Road" })))
            {
                rotation2 = new Vector3(rh1.collider.transform.eulerAngles.x, heading, rh1.collider.transform.eulerAngles.z);
                position2 = new Vector3(rh1.point.x, rh1.point.y, rh1.point.z);

                distance = Vector3.Distance(position1, position2);
            }

            timer = 0.0f;
            startLerp = true;
            numberOfPositions++;
            break;

        case 2:
            position1 = position2;
            rotation1 = rotation2;

            RaycastHit rh2;

            if (Physics.Raycast(newPosition + Vector3.up * 10000, Vector3.down, out rh2, float.PositiveInfinity, LayerMask.GetMask(new string[] { "Road" })))
            {
                rotation2 = new Vector3(rh2.collider.transform.eulerAngles.x, heading, rh2.collider.transform.eulerAngles.z);
                position2 = new Vector3(rh2.point.x, rh2.point.y, rh2.point.z);

                distance = Vector3.Distance(position1, position2);
            }

            timer = 0.0f;
            distance = Vector3.Distance(position1, position2);
            break;
    }
}

// Update is called once per frame
void Update()
{
    if (startLerp)
    {
        
        transform.position = Vector3.MoveTowards(position1, position2, distance * timer);
        transform.rotation = Quaternion.RotateTowards(Quaternion.Euler(rotation2), Quaternion.Euler(rotation2),Quaternion.Angle(Quaternion.Euler(rotation2), Quaternion.Euler(rotation2)) * timer);

        timer += Time.deltaTime;
    }
}

}

Intriguing.

I think your game is relatively complex, given that you’re using websockets to update transforms. So, I won’t ask to see more code (and get lost in a rabbit hole), but instead offer what’s worked for me on multiple projects.

  1. “Acceptable Performance” and XML rarely exist in the same world. Using XML with websockets is like putting unleaded gasoline in a NASCAR engine. It’ll work, but the car (websocket) was designed to run much, much leaner. The “leaner” and much more performant solution is JSON, or even better yet, your own custom and stream-lined format. That is, you’ll immediately get a 30%, or more, reduction in time parsing messages to/from the server. Your delays/lags, based on your code, is almost certainly NOT in Update/Unity. It’s the server calls.

  2. Pick, and test, your websocket server. I’ve used a few, Jetty, Tomcat, and others and there’s a significant difference in how they perform with regard to sending streams and/or not-simple-text messages. So, just send text (ex. session.getBasicRemote().sendText(msg.toString()):wink: whenever you can. Tomcat has worked great for me (and has been for about 18 years :slight_smile:

  3. I won’t put a link in, but HTTP pro, combined with protobuf, is a proven and well-performing combination. HTTP provides your websocket comms and protobuf stores your data on the client machine. Additionally, HTTP Pro will handle the server comms thread-separate for you so you don’t have to worry about blocking in your code.

  4. If your messages to/from the server are simple (short in length and quick to parse) you can probably get away with 3 or so server calls/update per second, and thus update your transforms 300% more frequently (reducing visual lag).

  5. more of a bitter observation…Multiplayer, online games that allow players to interact in the same scene, and without lag/jitterin/delays is just not possible for the masses with the current technology. Heck, even Unity’s example multiplayer Tanks game is disappointing when trying to play it over a real world WAN. There’s mayyyybe three successful MMO’s functioning on the planet with good, consisten and reliable performance, but if you have that kind of budget, resources and time, you probably have your own game engine. In short, online multiplayer, if that’s what you’re doing, will probably be disappointing unless all players can consistently and reliably get at least a 1Mbs connection (even though your data/messages may be small, the users probably have a brower open, email, and a few other apps sucking up the bandwidth, like Steam ;-).