Quick answer: Unity’s built-in Rigidbody interpolation buffers a pose between the previous and current physics step. Client-side prediction with rollback teleports the body backward and resimulates forward, but the interpolation buffer still holds the stale pose — producing visible smearing during corrections. Disable Rigidbody interpolation entirely on predicted objects, place the renderer on a child transform, and lerp that child toward the physics root in Update. Snap the child instantly when reconciliation happens.
You ship a netcode-flavored prototype with a Rigidbody-driven character. Locally everything looks fine. Add 80 ms of latency and turn on rollback prediction and the player starts to ghost — jittering between two positions every time the server sends a correction. The physics state is right. The collisions are right. The visible mesh is what is wrong, and the cause is the same one-line setting that made movement feel smooth in single-player: Rigidbody.interpolation = Interpolate.
The Symptom
The Rigidbody-driven object hops, smears, or rubber-bands every time a server snapshot disagrees with the client’s predicted state. The amount of visible glitching scales with latency and with how often you reconcile. On a LAN it is invisible; over the public internet it is unplayable. If you log transform.position directly inside FixedUpdate after reconciliation, the values look correct. The renderer is showing something else.
Symptoms get worse when you teleport the body to fix a desync (for example, snapping a character back to the server’s authoritative position). The body teleports correctly but the renderer streaks between the old and new locations for one or two frames before settling.
What Causes This
Unity’s Rigidbody runs in FixedUpdate at a fixed timestep (typically 50 Hz). The renderer runs at variable framerate in Update. Without help, the visual mesh would jump in chunks. Interpolation solves this by storing the pose at the end of the previous physics step and at the end of the current physics step, then lerping between them based on how much physics time has passed within the current rendering frame.
The buffer is invisible to your scripts. There is no API to clear it. When you do client-side prediction with rollback, the loop looks like this:
- Server sends authoritative state for tick N.
- Client teleports its Rigidbody back to that state.
- Client resimulates inputs from tick N + 1 forward to the present tick.
- The body lands at the corrected predicted position.
Every one of those teleports invalidates the interpolation buffer. The buffer still contains the pre-rollback pose, but the body is now at the post-rollback pose. The renderer lerps between an old wrong pose and a new correct one, producing the smear. Unity has no concept of “this teleport is intentional, please flush your buffer” because the physics engine cannot tell intentional teleports apart from large impulses.
The same problem appears with Rigidbody.MovePosition during prediction, with Physics.Simulate calls, and any time you manually drive the simulation.
The Fix
Step 1: Disable Rigidbody interpolation on predicted objects. In the inspector or in code, set the Rigidbody’s interpolation mode to None. This stops Unity from buffering the pose and from interfering with your reconciliation.
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class PredictedBody : MonoBehaviour {
private Rigidbody rb;
void Awake() {
rb = GetComponent<Rigidbody>();
// Critical: turn off Unity's pose buffering.
rb.interpolation = RigidbodyInterpolation.None;
}
}
Step 2: Move the renderer to a child object. Re-parent the MeshRenderer (and any visual-only colliders, particle effects, or weapon attachment points) under a new GameObject called Visual. The Rigidbody and physics colliders stay on the root.
Step 3: Lerp the visual child toward the physics root in Update. The physics object jumps in fixed steps. The visual smoothly chases it. This is the same smoothing Unity does internally, but you control when to flush it.
using UnityEngine;
public class VisualSmoother : MonoBehaviour {
[SerializeField] private Transform physicsRoot;
[SerializeField] private float followSpeed = 30f;
[SerializeField] private float snapDistance = 1.5f;
void LateUpdate() {
Vector3 targetPos = physicsRoot.position;
Quaternion targetRot = physicsRoot.rotation;
// If we are too far away, snap. This catches teleports and reconciliations
// that the smoother cannot reasonably catch up to.
if (Vector3.Distance(transform.position, targetPos) > snapDistance) {
transform.position = targetPos;
transform.rotation = targetRot;
return;
}
float t = 1f - Mathf.Exp(-followSpeed * Time.deltaTime);
transform.position = Vector3.Lerp(transform.position, targetPos, t);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRot, t);
}
}
Step 4: Snap on reconciliation. Whenever your netcode applies an authoritative correction, raise an event the smoother listens for, or simply call a public Snap() method that copies the physics pose to the visual immediately. This avoids the catch-up lerp painting a path through space the player never actually took.
public class VisualSmoother : MonoBehaviour {
// ... as before ...
public void Snap() {
transform.position = physicsRoot.position;
transform.rotation = physicsRoot.rotation;
}
}
// In your reconciliation step:
void ApplyServerState(NetworkedState state) {
rb.position = state.position;
rb.rotation = state.rotation;
rb.linearVelocity = state.velocity;
rb.angularVelocity = state.angularVelocity;
// Resimulate inputs from this tick forward.
ResimulateFromTick(state.tick);
// Tell the visual not to smear from the bad pose to the good one.
smoother.Snap();
}
Step 5: Tune the follow speed. A followSpeed around 25-40 feels responsive without showing the per-frame physics steps. Lower it for slower characters; raise it for snappy arcade feel. Keep snapDistance just above the largest reconciliation correction you expect under normal latency.
Why This Works
Unity’s Rigidbody interpolation was designed for single-player or non-predicted multiplayer where the body never lies about where it is. Once you start teleporting bodies for reconciliation, that buffer becomes a liability. Owning the smoothing yourself makes the rules explicit: the physics layer is the truth, the visual layer is a follower, and reconciliation tells both layers to update at the right time.
Splitting the transforms also gives you free benefits beyond prediction. Camera scripts can lock to the physics root for instant response while the visual catches up. Animations can be driven by the physics velocity without inheriting the smoothing lag. And debug visualizations always show the true authoritative position.
"If your physics body is being told to lie about its history every tick, do not also let the renderer lie about it. One source of truth, one smoother on top, and explicit snaps when truth changes."
Related Issues
If your Cinemachine camera adds its own interpolation jitter on top of physics motion, see Fix: Unity Cinemachine Camera Jitter. For coroutine-based input handling that misses fixed-step ticks during reconciliation, check Fix: Unity Coroutine Not Starting.
Physics is truth. Visual is a follower. Snap when truth changes.