PUN 2 Lag Compensation

NSDG | Nov 24, 2019 | 0 Comments

In Photon Network player synschronization is done by sending values over network at a specific rate.

For example to synchronize player position we need to send Vector3 for position and Quaternion for rotation, then when the values are received we apply them to transform values.

However since the values are sent in intervals, simply applying them to transform will result in choppy movement, that's when Vector3.Lerp and Quaternion.Lerp comes in place.

transform.position = Vector3.Lerp(transform.position, latestPos, Time.deltaTime * 5);
transform.rotation = Quaternion.Lerp(transform.rotation, latestRot, Time.deltaTime * 5);

But even this method have some disadvantages: Simply smoothing position and rotation will result in innacurate representation of player movement. For example in First Person Shooters where the movement need to be sharp and quick.

With that being said, below is improved version of position synchronization which takes into account networking time and tries to replicate the original movement as close as possible:

using UnityEngine;
using Photon.Pun;

public class PUN2_LagFreePlayerSync : MonoBehaviourPun, IPunObservable
    //Values that will be synced over network
    Vector3 latestPos;
    Quaternion latestRot;
    //Lag compensation
    float currentTime = 0;
    double currentPacketTime = 0;
    double lastPacketTime = 0;
    Vector3 positionAtLastPacket = Vector3.zero;
    Quaternion rotationAtLastPacket = Quaternion.identity;

    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
        if (stream.IsWriting)
            //We own this player: send the others our data
            //Network player, receive data
            latestPos = (Vector3)stream.ReceiveNext();
            latestRot = (Quaternion)stream.ReceiveNext();

            //Lag compensation
            currentTime = 0.0f;
            lastPacketTime = currentPacketTime;
            currentPacketTime = info.SentServerTime;
            positionAtLastPacket = transform.position;
            rotationAtLastPacket = transform.rotation;

    // Update is called once per frame
    void Update()
        if (!photonView.IsMine)
            //Lag compensation
            double timeToReachGoal = currentPacketTime - lastPacketTime;
            currentTime += Time.deltaTime;

            //Update remote player
            transform.position = Vector3.Lerp(positionAtLastPacket, latestPos, (float)(currentTime / timeToReachGoal));
            transform.rotation = Quaternion.Lerp(rotationAtLastPacket, latestRot, (float)(currentTime / timeToReachGoal));
  • Attach the script above to your Player instance and assign it to PhotonView Observed Components.