This document explains how client prediction works in the interactions package and how it affects various interaction systems.
Full vs Partial Ticks
Most interaction systems run in the PredictedFixedStepSimulationSystemGroup
, which only executes on full network ticks. This approach has several benefits:
- Ensures consistent behavior across clients and server
- Coordinates interactions with physics systems, which also run in fixed step groups
- Reduces CPU load by not processing every partial tick
- Avoids potential hitches when partial ticks run shortly after physics-dependent full ticks
Some systems, particularly those involving visual feedback, do run on partial ticks:
- Movement smoothing systems for grabbable objects
- Visual feedback systems like hover indicators
- Systems that need to appear more fluid to the user
Using IsFirstTimeFullyPredictingTick
Some systems use NetworkTime.IsFirstTimeFullyPredictingTick
to ensure certain operations only happen once per prediction loop. This is particularly important for:
- Event-based systems that shouldn't trigger multiple times
- State changes that should only occur once
- Operations that modify component data that other systems depend on
Example from the GrabbableSystem
:
public void OnUpdate(ref SystemState state)
{
var networkTime = SystemAPI.GetSingleton<NetworkTime>();
if (!networkTime.IsFirstTimeFullyPredictingTick)
{
return;
}
}
Simulation Rate Impact
The simulation tick rate directly affects the responsiveness of interactions since most systems only run on full ticks. Key considerations:
- Higher tick rates provide more responsive interactions but increase CPU and bandwidth usage
- Lower tick rates may cause interactions to feel less responsive
- The simulation rate can be adjusted in
NetCodeConfig
- Visual smoothing can help mask lower tick rates
- For grabbable objects, increased smoothing may be needed at lower tick rates
Local vs Networked Movables
The system handles two types of movable entities differently:
Local Movables
- Run in the
SimulationSystemGroup
- Not affected by prediction/rollback
- Used for non-networked entities like local player rigs
- Process every frame for smooth movement
Networked Movables
- Run in
PredictedSimulationSystemGroup
- Subject to prediction and rollback
- Include all ghost entities
- Process on full and partial ticks
Handling Mispredictions
Since clients predict ahead of the server, some mispredictions are inevitable. The interaction systems includes some mechanisms to handle these:
Prediction Smoothing
public static void SmoothingAction(IntPtr currentData, IntPtr previousData, IntPtr userData)
{
ref var current = ref UnsafeUtility.AsRef<Movable>((void*)currentData);
ref var previous = ref UnsafeUtility.AsRef<Movable>((void*)previousData);
var positionDist = math.distancesq(current.TargetPosition, previous.TargetPosition);
if (!(positionDist > 0))
{
return;
}
current.TargetPosition = math.lerp(previous.TargetPosition, current.TargetPosition, SmoothingFactor);
current.TargetRotation = math.slerp(previous.TargetRotation, current.TargetRotation, SmoothingFactor);
}
Common Misprediction Scenarios
- Released grabbables may snap back to server position
- Interactor bindings may briefly show incorrect states
- Movement paths may need correction
Mitigation Strategies
- Use smoothing components where appropriate
- Increase simulation rate, trading off on performance
- Design interactions to be less sensitive to small positional differences
- Consider using client-side visual feedback that doesn't require prediction
Debugging Prediction Issues
Unity's Network Debugger (Window > Multiplayer > Network Debugger) is a valuable tool for tracking and visualizing mispredictions in your application. Using this tool, you can:
- Monitor network tick rates and latency
- Identify specific frames where mispredictions occur
- Debug server reconciliation issues
This tool is particularly useful when tuning interaction parameters like smoothing values or investigating specific misprediction scenarios.