Physics
The Netcode package has some integration with Unity Physics which makes it easier to use physics in a networked game. The integration handles interpolated ghosts with physics, and support for predicted ghosts with physics.
This works without any configuration but will assume all dynamic physics objects are ghosts, so either fully simulated by the server (interpolated ghosts), or by both with the client also simulating forward (at predicted/server tick) and server correcting prediction errors (predicted ghosts), the two types can be mixed together. To run the physics simulation only locally for certain objects some setup is required.
Physics Ghost Setup Checklist:
GhostAuthoringComponent | Rigidbody | Static | In a separate physics world | ------------ |
---|---|---|---|---|
yes | yes | both | no | valid |
no | yes | no | yes | valid |
not required | no | yes | both | valid |
yes | both | yes | both | valid |
no | on a child of a ghost | no | both | error |
no | yes | no | no | error |
Important: For physics to run at all, Netcode currently requires at least a single predicted ghost to exist in your scene. With this, the prediction update loop will run and tick the physics loop.
Interpolated ghosts
For interpolated ghosts it's important that the physics simulation only runs on the server.On the client, the ghost's position and rotation are controlled by the snapshots from the server and the client shouldn't run the physics simulation for interpolated ghosts.
To make sure this is true, Netcode disables the Simulate
component data tag on clients on appropriate ghost entities at the beginning on each frame. This makes the physics object kinematic
and they won't be moved by the physics simulation.
In particular:
- The
PhysicsVelocity
is ignored (set to zero). - The
Translation
andRotation
are preserved.
Predicted ghosts and physics simulation
Predicted physics means that the physics simulation runs in the prediction loop (possibly multiple times per update from the tick of the last received snapshot update) on the client, as well as running normally on the server.
During initialization, Netcode moves the PhysicsSystemGroup
and all FixedStepSimulationSystemGroup
systems into the PredictedFixedStepSimulationSystemGroup
. This group is the predicted version of FixedStepSimulationSystemGroup
, so everything here will be called multiple times up to the required number of predicted ticks. These groups are then only updated when there is actually a dynamic predicted physics ghost present in the world.
All predicted ghosts with physics components run this kind of simulation when they are dynamic. Like with interpolated ghosts, the Simulate
tag will be enabled/disabled as appropriate at the beginning of each predicted frame, but this time multiple simulation steps might be needed.
Since the physics simulation can be quite CPU intensive, it can spiral out of control when it needs to run multiple times. Needing to predict multiple simulation frames could then result in needing to run multiple ticks in one frame as the fixed timesteps falls behind the simulation tick rate, making the situation worse. On the server it can be beneficial to enable simulation batching in the ClientServerTickRate
component, see the MaxSimulationStepBatchSize
and MaxSimulationStepsPerFrame
options. On clients there are options for prediction batching exposed in the ClientTickRate
, see MaxPredictionStepBatchSizeFirstTimeTick
and MaxPredictionStepBatchSizeRepeatedTick
. However, this WILL increase the chance of mispredictions.
By default, the current quantization level is set to 1000 for transform and velocity. This is enough in most cases, but does create discrepancies in simulation which can create visible corrections or jitter. Increasing quantization for physics ghosts will result in more precise simulations at the cost of more bandwidth consumption.
Using lag compensation predicted collision worlds
When using predicted physics the client will see his predicted physics objects at a slightly different view as the correct authoritative view seen by the server, since it is forward predicting where objects will be at the current server tick. When interacting with such physics objects there is a lag compensation system available so the server can look up what collision world the client saw at a particular tick (to for example better account for if he hit a particular collider). This is enabled via the EnableLagCompensation
tick in the NetCodePhysicsConfig
component. Then you can use the PhysicsWorldHistorySingleton
to query for the collision world at a particular tick.
Multiple physics worlds
Predicted simulation will work by default and all physics objects in the world should be ghosts. To enable client-only physics simulation (for example to use it run VFX, particles and any other sort of physics interaction that does not need to be replicated) another physics world needs to be created for it.
By default, the main physics world at index 0 will be the predicted physics world, but a separate client-only physics world can also be created, running it's own distinct simulation. This can be done by implementing a custom physics system group and providing it with a new physics world index. Creating a client only physics world at index 1 can be done most simply like this:
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
public partial class VisualizationPhysicsSystemGroup : CustomPhysicsSystemGroup
{
public VisualizationPhysicsSystemGroup() : base(1, true)
{}
}
Where the arguments to the custom class constructor are the world index and boolean indicating if it should share static colliders with the main physics world. Physics simulation will here run in the usual FixedUpdateSimulationGroup
as usual. See more about the CustomPhysicsSystemGroup
in the Unity Physics documentation.
The two simulations can use different fixed-time steps and are not required to be in sync, meaning that in the same frame it is possible to have both, or only one of them to be executed independently. However, as mentioned in the previous section, for the predicted simulation when a rollback occurs the simulation may runs multiple times in the same frame, one the for each rollback tick. The client only simulation of course just runs once as normally.
When a client only physics world exists, all non-ghost dynamic physics objects can be moved to that. This can be configured in the NetCodePhysicsConfig
component which can be added to a subscene. By setting the ClientNonGhostWorldIndex
there to the client only physics world index all dynamic non-ghosts will be moved there.
As part of the entity baking process, a PhysicsWorldIndex shared component is added to all the physics entities, indicating in which world the entity should be part of.
Note
It is the responsibility of the user to setup their prefab properly to make them run in the correct physics world. This can be achieved with the PhysicsWorldIndexAuthoring
component, provided with the Unity Physics package, which allows setting the physics world index for rigid bodies. For more information, please refer to the Unity Physics documentation.
Interaction between predicted and client-only physics entities
There are situation when you would like to make the ghosts interact with physics object that are present only on the client (ex: debris). However, them being a part of a different simulation islands they can't interact with each-other.
The Physics package provides for that use-case a specific workflow using Custom Physics Proxy
entities.
For each physics entity present in the predicted world where you would like to interact with the client-only world, you need to add the CustomPhysicsProxyAuthoring
component. The baking process will then automatically create a proxy entity with the necessary physics components (PhysicsBody, PhysicsMass, PhysicsVelocity) along with a CustomPhysicsProxyDriver
which is the link to the root ghost entity. It will make a copy of the ghosts collider as well and configure the proxy physics body as kinematic. The simulated ghost entity in the predicted world will then be used to drive the proxy by copying the necessary component data and setup the physics velocity to let the proxy move and interact with the other physics entities in the client-only world.
The ghost proxy position and rotation and are automatically handled by SyncCustomPhysicsProxySystem
system.
By default the kinematic physics entity is moved using kinematic velocity, by altering the PhysicsVelocity component. It is possible to change the default behavior for the prefab by setting the
GenerateGhostPhysicsProxy.DriveMode
component property.
Furthermore, it is possible to change that behavior dynamically at runtime by setting the PhysicsProxyGhostDriver.driveMode
property to the desired mode.
Limitations
As mentioned on this page there are some limitations you must be aware of to use physics and Netcode together.
- Physics simulation will not use partial ticks on the client, you must use physics interpolation if you want physics to update more frequently than it is simulating.
- The Unity.Physics debug systems does not work correctly in presence of multiple world (only the default physics world is displayed).