In this lesson, you will learn how to diagnose and resolve movement jitter by comparing local client-side movement predictions against the server’s authoritative state.
The Source of Jitter
When you move locally in a networked game, the client "predicts" its own movement instantly to hide latency. Meanwhile, the server calculates the "true" position and sends it back. If the client’s prediction differs from the server’s authoritative result by more than an allowed tolerance threshold, Unreal Engine triggers a Reconciliation event. The jitter you see occurs when the client's position is "snapped" back to the server's corrected location, effectively overwriting the local prediction.
Visualizing the Discrepancy
To analyze these errors, you must observe the gap between SavedMoves and AcknowledgedMove. A prediction error typically occurs when the client's internal simulation state (like velocity or acceleration) drifts from the server's state, often due to floating-point errors, input loss, or conflicting movement logic.
You can log these discrepancies during the ClientAckGoodMove phase to see if your local PerformMovement logic is diverging from the server:
void UMyCharacterMovementComponent::ClientAckGoodMove(float Timestamp)
{
// Compare the client-side saved location with the acknowledged server location
FVector ClientLoc = GetSavedMove(Timestamp)->GetLocation();
FVector ServerLoc = GetAcknowledgedServerLocation();
float ErrorDistance = FVector::Dist(ClientLoc, ServerLoc);
if (ErrorDistance > NetworkSmoothingThreshold)
{
UE_LOG(LogNet, Warning, TEXT("Prediction error detected: %f units"), ErrorDistance);
}
}
Resolving the Conflict
When an error is detected, the engine forces the client to re-simulate all pending moves that haven't been acknowledged by the server yet. If you are experiencing constant micro-stuttering, the issue often stems from inconsistent input processing or non-deterministic state variables used during the movement update. Ensure that all movement logic is strictly confined to the CharacterMovementComponent and that you aren't modifying the character's transform directly via RPCs, which bypasses the prediction system entirely.
Key Takeaway: Jitter is the visual manifestation of reconciliation; by logging the distance between the client's SavedMove and the server's AcknowledgedMove, you can isolate the specific movement logic causing your simulation to drift.