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 观察组件。