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);
}
}
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):
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
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.
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
[sorry for the thread reap]
Just reading this, I noted a few small things
Your impulse calculations seem correct, so I am confused why they’d be so low as to register like a bug. Also, (how) do you avoid double counting or accidentally canceling out the impulses if both players have this?
On my own test project if I set an object the players can spawn to be server auth, they still only seem to be able to push the ones they spawned, so if you wanted to be able to say have player A throw an object into player B or an object owned by player B, you’d need this function on those too, right?
I’m just trying to sort out some very basics with networked physics and it seems like a nightmare trying to just have two rigidbodies hit each other, dynamic ownership changes cause ‘two players cant touch the same object’, and this causes ‘you need to attach an extra script that sorta fakes physics to every rigidbody in the game’
So its been a while since I worked on this project, but really it just comes down to this being a complicated problem to solve.
I only had so much time to work on the project, so I settled for a server authoritative model where all inputs were sent to the server, and the server would calculate the results and send them back to the clients. Player input, of course, didn’t feel super responsive, but collisions felt predictable and looked great. At the time I felt that was more important.
I know there ways of doing this more seamlessly, the Rocket League GDC above touches on that, but those solutions are substantially more complex. I’m just a hobbyist messing around.
I ended up putting the project up on Steam for free if you’re curious. Its only a little bit janky .