Need some help figuring out a network architecture for physics based game

Recently, I’ve created a physics based, player-controllable drone. They work with PID control of simulated electric motors, where forces are applied to a rigidbody, proportionally to the speed of rotation of the motor:

I want to work on adding multiplayer, and for context I’m only hoping to have a maximum of 6-10 players. However, I’m not sure of the best way of going about synchronisation, and don’t want to build up a lot of technical debt, by barking up the wrong tree…

I’m not yet set on a host vs standalone server architecture, so would like to know about the advantages and disadvantages of either.

To add one last layer of complexity, the goal is to have the drones able to pick up synchronised objects in the world, and I would also like the drones to be collidable if possible.

Would also appreciate advice on how to go about adding netcode to my existing system : I have my drone as a prefab with the controller that currently reads the player inputs, rigidbody component, system for grabbing objects, etc. Would I be better to modify the controller script to include the necessary netcode? Or should I build a separate drone netcode script that handles whatever synchronisation is necessary, and passes player inputs to the drone controller where required?

From what I know about multiplayer networking (which is not a lot), I believe that I have three options for synchronisation:

  1. Each client simulates only their drone, and sends its position (and maybe velocity for extrapolation?) to the server. After this, the server broadcasts all the positions of all the drones is broadcast to the clients, to update the positions of each client’s drone instances. - presumably in this instance I would need to disable the physics components on the client’s other drone instances, and treat it as a collider that just magically moves around the world.

  2. The sever simulates all the drones, and each client sends inputs to the host/server for their drone, which is then applied to each drone, and its position/velocity is broadcast back to each client. I’m guessing with this system, latency is the main problem, but presumably synchronisation of picking up objects becomes easier since there’s no contention when two clients try to grab something at the same time?

  3. Each client simulates all of the drones in the game, and then sends positions to the server. The server/host then resolves discrepancies between drone positions accross clients, either by picking randomly or the server also simulates all of the drones, and uses its simulated versions to adjust discrepancies. This seems like it’d be much more heavier on processing and network traffic for the client and the server, but I’m guessing offers the combined advantages of low latency for the client while using allowing the server to resolve conflicts?

I’m very sure that I’ve got stuff wrong or missed an alternative method, so any and all advice on this topic is appreciated. Would love to hear about the various advantages/disadvantages and relative ease of implementation.

Thank you so much for taking the time to read!

1 Like

A client authoritative NetworkTransform should do (option 1). This gets automatically synchronized to clients.

The server will handle the collision and separation of drones due to their colliders but it will not feel “correct”. It depends on how much drone to drone collision matters in the project, perhaps it suffices that they won’t occupy the same space and a little back and forth jitter as they struggle to make place for each other is acceptable.

When picking up, I suppose you already have the drone to drone collision preventing other drones from picking up the item eg only the one closest may pick it up. If not then two clients trying to pick up the item would still result in one of the drones to get the item since packets are not processed in parallel on the server. You just need to handle the case where the item pickup request is for an item that has already been picked up in which case you deny the pickup.

Generally I would advice towards separating the Netcode from all the other code. It gets really messy if you have a script with even just slightly complex logic, and then that script also includes RPCs that are both server and client side, and neither of the RPCs should have access to certain fields.

One of the biggest headaches you’ll find with RPCs is that a script may have say a dozen fields but only some fields are used by the code that runs server-side and others are actually off-limits on the client side. Worse even if the RPCs themselves make calls to other methods in the same script so you need to keep thinking about whether that method may on the server, the client or both. It’s a code smell if many of your methods include conditionals like IsOwner, IsServer and similar.

For this reason it can actually be very helpful to separate Netcode into client and server (and even owner) scripts where the client script calls an RPC on the server script and vice versa, thus they encapsulate their fields and behaviour.

Thanks so much for the reply, really appreciate the advice on separating out netcode! Just to mention, since making this post I’ve done a little reading and gone through most of the recent E-book on multiplayer published by unity.

I would be inclined to say that drone-drone collision isn’t a core feature. The plan is to have other gameplay features to encourage separation of players, and it’s more that I want the collision to sort of work, in the eventuality that collision occurs, and it doesn’t completely destroy immersion.

One thing that I’m still trying to resolve, I’m not sure about the objects being picked up by the drones? The plan is for these to also be physics based - though with almost no mass, so essentially not contributing any momentum to collisions with players. Since there’s no player control being applied, it doesn’t make sense to me that they are client authoritative? I’m not sure I understand how synchronisation would even work if each client had their own authority over a shared physics based object. So should these objects be server authoritative? or implement some form distributed authority, where authority switches based on the player that has currently grabbed the object?

Coming back to the drones: To get better collision performance, how could I use a synchronised rigidbody velocity and acceleration to build in some basic client-side prediction for the result of collisions before updates come from other clients? Or I could switch to server authority if I think one drone will hit another? chances are a player won’t be able to stop the collistion in that instance before it happens anyhow right, and then resolve the collision on the server, before switching back to client authority once the server thinks the drones aren’t colliding anymore?

A good solution for pickup items that is easy to implement and avoids plenty of issues is to actually have two objects:

  • the networked item visible in the scene
  • the non-networked, visual-only item parented to a networked object

The way pickup works is to request to the server to pick up the item. Now the server could move that network object somewhere off screen or use network visibility to hide it for everyone. The server also confirms the pickup with an RPC so that all clients will enable (or instantiate) the visualization of the pickup item to the targeted drone.

Dropping the item is the reverse, with the networked in-scene item being moved to an appropriate position just below the drone and made visible again.

The networked item prefab is best designed in such a way that it contains only

  • root (NetworkObject and other components)
    • Item Mesh (the visualization, may be a separate prefab)
    • Collider (for the pickup collision/raycast)

The Item Mesh is what you would instantiate and parent to the drone. This makes the item perfectly stick to the drone for all clients without being affected by latency and without having to net sync anything about it (network parenting should be avoided, it’s a major headache).

The collider is separate to avoid having to explicitly disable the collider for the attached item.

If you have many different items, I would not make individual prefabs but rather omit the Item Mesh from the prefab. When placed in the scene, you could then have a dropdown or reference field in Inspector to select or assign the Item Mesh so that you can use the same Item Prefab for all item variants and they’ll share the same collider for consistency.

Collision and resolve is done on the server only. The client’s collision is not relevant as the other drone will be in a different place for each client due to latency. It will still not look accurate if the server handles it but at least the collision response is affected only by each client’s latency to the server, not the client’s latency to the server and then also the latency from server to the other client.

1 Like