We currently are working on a multiplayer game with Unity Netcode. The game is a racing game with relatively high speed. The player objects get synchronized with the Network Transform component.
We have the problem that the player is lagging a little bit (or junpig some steps forward instead of moving smooth). The interpolate setting on the Network Transform makes it a little bit better but not completely.
Something we could observe is that when the own camera isn’t moving, the other player isn’t lagging at all, but since we have a racer game the camera moves all the time.
Hi @Darius0o2 , what you need is Client side prediction + Reconciliation.
This is not available in Netcode For GameObjects out of the box, but if you google it you’ll be able to find sevral relevant articles on how to implement it for you specific use case.
Hope this helps ^.^
Thank you, I’m gonna look into it
We finally got the client side prediction to work and it eleminated our problem.
The following side helped us a lot with understanding how it works and the implementation: https://codersblock.org/posts/unity-client-side-prediction/
How did u managed to make it, ive been struggling long time ago to implement Client-Side-Prediction
[NGO] Ball Movement Appears Jittery or Laggy for All Clients in Multiplayer Cricket Game
Hi everyone,
I’m currently developing a multiplayer cricket game using Unity Netcode for GameObjects (NGO), and I’m facing an issue where the ball movement appears jittery or laggy for all clients during gameplay.
Setup:
The ball prefab has a NetworkObject and a NetworkTransform component.
The server is authoritative — it instantiates and spawns the ball for all clients.
The server handles all physics calculations and applies the ball’s movement.
The clients only send input (for example, the bowl action) to the server and receive position updates through the NetworkTransform.
Issue:
Even though the ball moves correctly across the network, all clients (including the host) see the ball movement as jittery or laggy.
It doesn’t appear smooth when the ball travels after being bowled.
Code Overview:
public void ReleaseBall()
{
if (!IsOwner) return;
Vector3 releasePos = localBall.transform.position;
Debug.Log("Accurate releasePos : " + releasePos);
LaunchBallServerRpc(releasePos);
}
This function is called in the bowler animation event at the last frame.
[ServerRpc(RequireOwnership = false)]
public void LaunchBallServerRpc(Vector3 releasePos)
{
GameManager.Instance.currentBall.transform.localScale = new Vector3(2.5f, 2.5f, 2.5f);
GameManager.Instance.RequestLaunchBall(releasePos);
}
public void RequestLaunchBall(Vector3 ballReleasePos)
{
Debug.Log("ballReleasePos : " + ballReleasePos);
BallPhysics.instance.BallPhysicsDatas(bowlerData[playerIndex], matchData);
BallPhysics.instance.Calculate();
Vector3 releasePos = ballReleasePos;
Vector3 desiredLanding = aim.position;
currentBall.transform.position = ballReleasePos;
var netTransform = currentBall.GetComponent<NetworkTransform>();
netTransform.Teleport(ballReleasePos, currentBall.transform.rotation, currentBall.transform.localScale);
currentBall.GetComponent<Rigidbody>().isKinematic = false;
NotifyEnableBallClientRpc();
float accuracyDeviation = BallPhysics.instance.accuracyDeviation * (1f / BallPhysics.instance.ConsistencyFactor());
accuracyDeviation *= 1f + (BallPhysics.instance.AggressionFactor() * 0.15f);
float aggressionBoost = 1f + (BallPhysics.instance.AggressionFactor() - 0.75f) * 0.2f;
BallPhysics.instance.finalSpeed *= aggressionBoost;
float accuracy = bowlerData[playerIndex].skills.accuracy;
Vector3 finalTarget = ApplyAccuracyOffset(desiredLanding, BallPhysics.instance.accuracyDeviation, accuracy);
currentBall.GetComponent<BallMotionController>().Init(
BallPhysics.instance.gameObject,
releasePos,
finalTarget,
BallPhysics.instance.finalSpeed
); // For Server
}
[ClientRpc]
void NotifyEnableBallClientRpc()
{
Debug.Log(“NotifyEnableBallClientRpc”);
currentBowler.GetComponent().localBall.GetComponent().enabled = false;
GameManager.Instance.currentBall.GetComponent().enabled = true;
}
Vector3 ApplyAccuracyOffset(Vector3 target, float accuracyDeviation, float accuracyPercent)
{
target.x += UnityEngine.Random.Range(-accuracyDeviation, accuracyDeviation);
accuracyPercent = Mathf.Clamp(accuracyPercent, 0f, 100f);
float maxError = Mathf.Lerp(1.0f, 0.2f, accuracyPercent / 100f);
float errorMultiplier = 1f - (accuracyPercent / 100f);
float angle = UnityEngine.Random.Range(0f, Mathf.PI * 2f);
float radius = Mathf.Sqrt(UnityEngine.Random.value) * maxError;
Vector3 scatterOffset = new Vector3(Mathf.Cos(angle), 0f, Mathf.Sin(angle)) * radius;
print("landing + scatterOffset " + target + " " + scatterOffset);
return target + scatterOffset;
}
public void Init(GameObject physicsObj, Vector3 start, Vector3 end, float speed) // For server
{
if (!ApplicationController.Instance.IsSinglePlayer)
{
if (!IsServer)
{
return;
}
}
Debug.Log("Init");
BallPhysics physics = physicsObj.GetComponent<BallPhysics>();
physicsGlobal = physics;
startPosGlobal = start;
endPosGlobal = end;
float distance = Vector3.Distance(start, end);
flightTime = distance / (speed / 3.6f); // km/h to m/s
elapsed = 0f;
if (GameManager.Instance.bowlerType == "pace")
swingTotal = physics.swingAmount;
else
swingTotal = physics.spinTurn;
Vector3 horizontal = new Vector3((end.x - start.x) / flightTime, 0, (end.z - start.z) / flightTime);
float vy = (end.y - start.y - 0.5f * Physics.gravity.y * flightTime * flightTime) / flightTime;
velocity = horizontal + Vector3.up * vy;
BallMotionController currentBall = GameManager.Instance.currentBall;
rb.linearVelocity = velocity;
baseVx = horizontal.x;
baseVz = horizontal.z;
active = true;
}
private void OnTriggerEnter(Collider other)
{
if (!IsServer)
{
return;
}
if (other.CompareTag(“Pitch”))
{
Debug.Log(“OnCollisionEnterPitchBefore”);
if (hasBounced) return;
Debug.Log("OnCollisionEnterPitch");
OnBallHitGround?.Invoke(this);
print("after velocity " + rb.linearVelocity);
rb.linearVelocity = BallPhysics.instance.HandleBounce(rb.linearVelocity, GetSpeed());
hasBounced = true;
}
}
Ball Physics Class:
(Include your full BallPhysics class here as you already have it written.)
Question:
I’m not sure why the ball movement appears jittery or laggy on all clients, even though the physics are handled on the server and the position updates are sent through NetworkTransform.
Is there a better way to handle physics-based movement with NGO to ensure smooth motion?
Should I use ClientNetworkTransform, custom interpolation, or another method to make the ball travel smoothly?
Are there any best practices for handling fast-moving Rigidbody objects in a server-authoritative setup?
Any suggestions or guidance would be greatly appreciated.