PUN 2 滞后补偿

Photon Network 中,玩家同步是通过网络以数据包的形式发送值来完成的。

例如,为了同步玩家位置,我们需要发送 Vector3 来表示位置,发送四元数来表示旋转,然后当接收到这些值时,我们将它们应用于变换。

然而,由于这些值是按时间间隔发送的,因此简单地将它们应用于变换将导致不稳定的运动,这就是 Vector3.Lerp 和 Quaternion.Lerp 发挥作用的地方。

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

但即使这种方法也有一些缺点:简单地平滑位置和旋转会导致玩家运动的不准确表示,这不太适合某些类型的游戏,其中精度很重要。

下面是位置同步的改进版本,它考虑了联网时间并尝试尽可能接近地复制原始运动:

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
            stream.SendNext(transform.position);
            stream.SendNext(transform.rotation);
        }
        else
        {
            //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));
        }
    }
}
  • 上面的脚本附加到您的 Player 实例并将其分配给 PhotonView 观察组件。
推荐文章
使用 PUN 2 制作多人汽车游戏
Unity 将多人聊天添加到 PUN 2 房间
使用 PUN 2 通过网络同步刚体
使用 PUN 2 在 Unity 中制作多人游戏
光子网络(经典)初学者指南
在 Unity 中构建多人网络游戏
多人数据压缩和位操作