Syncing collisions with Client Authorative Rigidbodies

Hello!

Ive been working on making a multiplayer driving sumo-wrestling game. Ive been working on a couple implementations to sync the players in my game. The players are all rigidbodies and need to respond to collisions with other players.

I started with a Sever Authoritative approach, but found this lacking and controlling the car wasn’t responsive enough.

So I’ve switched to a Client Authoritative approach. I plan on handling collision calculations on the server, and then sending the results to the clients so the proper collision forces can be applied. This is where I am having trouble. The rigid bodies do not respond to any force applied to them!

I have some debugging statements included in this block and I can confirm that after a collision I see the an appropriate force vector logged on the client session.

So what can I be missing? Is there a proper way for the server to apply force to a game object? I understand with this model, the server can not directly change the client position, but shouldn’t a ClientRpc allow the client to make this adjustment. I feel like I am misunderstanding something in the configuration of my Network Objects

Here is a sample from the Controller Script and the network objects on my Player.

 private void OnCollisionEnter(Collision collision)
    {
        collision.gameObject.TryGetComponent<HogController>(out HogController collisionTarget);
        if (IsServer && collisionTarget != null)
        {
            // Get the NetworkRigidbody components
            Rigidbody rb1 = rb;
            Rigidbody rb2 = collision.gameObject.GetComponent<Rigidbody>();

            CarState collisionTargetState = collisionTarget.GetClientState();

            // Calculate the relative velocity
            Vector3 relativeVelocity = state.peakVelocity - collisionTargetState.peakVelocity;

            // Calculate the masses
            float mass1 = rb1.mass;
            float mass2 = rb2.mass;

            // Calculate the impulse
            Vector3 impulse1 = (mass2 * relativeVelocity) / (mass1 + mass2);
            Vector3 impulse2 = -impulse1;

            ulong player1Id = GetComponent<NetworkObject>().OwnerClientId;
            ulong player2Id = collision.gameObject.GetComponent<NetworkObject>().OwnerClientId;

            Debug.Log(message:
                "Collision detected between " + ConnectionManager.instance.GetClientUsername(player1Id)
                 + " and " + ConnectionManager.instance.GetClientUsername(player2Id)
            );

            Debug.Log("Collision Relative Velocity - " + relativeVelocity);

            // Notify clients about the collision and applied force
            ApplyCollisionForceClientRpc(player1Id, impulse2);
            ApplyCollisionForceClientRpc(player2Id, impulse1);
        }
    }

    [ClientRpc]
    private void ApplyCollisionForceClientRpc(ulong clientId, Vector3 force)
    {
        if (IsOwner && clientId == GetComponent<NetworkObject>().OwnerClientId)
        {
            Debug.Log(message: 
                "Applying collision force of " + force * additionalCollisionForce + " to " + ConnectionManager.instance.GetClientUsername(clientId)
            );
            var client = NetworkManager.Singleton.ConnectedClients[clientId];
            client.PlayerObject.GetComponent<Rigidbody>().AddForce(force * additionalCollisionForce, ForceMode.Impulse);
        }
    }

Unity v6000.0.33f1 - NGO v2.2.0

Are they set to be kinematic? At runtime, if you have a NetworkRigidbody component on the object, that component will toggle any non-authoritative instances to be kinematic. And Kinematic bodies ignore any forces, instead you can only modify them through setting linearVelocity and angularVelocity directly.

Generally speaking, what you are attempting is a serious challenge - the correct synchronization of dynamic rigidbody collisions. No amount of once-only push-back impact forces will resolve your problem in a satisfactory way, in fact it’s likely going to explode the simulation due to latency. Imagine a situation where one player sits almost perfectly between two others, then collides with one, gets pushed back into the collision of another, gets pushed back in the opposite direction, and so forth …

It would be instructive to watch this presentation to see to what great lengths Rocket League developers had to go in order to ensure their game’s relative stability (provided everyone is playing with low pings):

Thank you for the response and for the video.

Are they set to be kinematic?

This was my first thought, but no. They are not kinematic. I have double checked in the scene view as well as printing out the kinematic state at the time of the collision.

I have also experimented with disabling “Auto Update Kinematic State” on the Network Rb component. This seems to make all players non kinematic on client and server, but still doesn’t seem to resolve the issue :thinking:

image

I just watched the video in full. Great recommendation. I fully understand that this is not a simple topic, but I am not opposed to spending the time I need to get a basic implementation of this working. Ultimately, I don’t have the same ambitious goals as Rocket League. This game isn’t meant to be competitive, and interactions between players dont have to be the most predictable.

Still I am curious about the ability to scale my game to include some of these more involved systems. If I wanted to implement a prediction model for example, Is that something that is even really supported by the current Network Transform? It seems I would need to forgo this solution and design my own handler for the vehicles and make calls to update, predict, and roll-back position states.

Thanks again for your time :slight_smile:.

I suppose I found the solution. The above code was working as intended, but the force values were simply too low to move my heavy cars. I simplified the equation by removing mass and just adding a force multiplier to the relative velocity. Seems to be working decently now :slight_smile: