Skip to content

Instantly share code, notes, and snippets.

@cnsoft
Created November 22, 2013 02:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cnsoft/7593906 to your computer and use it in GitHub Desktop.
Save cnsoft/7593906 to your computer and use it in GitHub Desktop.
> CoreTech 这部分应该才是核心的点. 插槽式同步. 客户端预测.服务器端纠正. >发送过来的都进缓冲.带时间戳. 本地更新时 依次取有效缓冲点,做Lerp 插值. 延迟太高的话, 服务器发过来的会直接把一些过时的插槽去掉. 客户端更新时依旧是Lerp. 可以拉回到某位置.不会超出太多.
using UnityEngine;
using System.Collections;
public class NetworkInterpolatedTransform : Photon.MonoBehaviour
{
//
// NOTE: Network interpolation is afffected by the network sendRate.
// By default this is 10 times/second for OnSerialize. (See PhotonNetwork.sendIntervalOnSerialize)
// Raise the sendrate if you want to lower the interpolationBackTime.
//
public double interpolationBackTime = 0.15;
internal struct State
{
internal double timestamp;
internal Vector3 pos;
internal Quaternion rot;
}
// We store twenty states with "playback" information
State[] m_BufferedState = new State[20];
// Keep track of what slots are used
int m_TimestampCount;
void Awake()
{
if (photonView.isMine)
this.enabled = false;//Only enable inter/extrapol for remote players
}
void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
// Always send transform (depending on reliability of the network view)
if (stream.isWriting)
{
Vector3 pos = transform.localPosition;
Quaternion rot = transform.localRotation;
stream.Serialize(ref pos);
stream.Serialize(ref rot);
}
// When receiving, buffer the information
else
{
// Receive latest state information
Vector3 pos = Vector3.zero;
Quaternion rot = Quaternion.identity;
stream.Serialize(ref pos);
stream.Serialize(ref rot);
// Shift buffer contents, oldest data erased, 18 becomes 19, ... , 0 becomes 1
for (int i = m_BufferedState.Length - 1; i >= 1; i--)
{
m_BufferedState[i] = m_BufferedState[i - 1];
}
// Save currect received state as 0 in the buffer, safe to overwrite after shifting
State state;
state.timestamp = info.timestamp;
state.pos = pos;
state.rot = rot;
m_BufferedState[0] = state;
// Increment state count but never exceed buffer size
m_TimestampCount = Mathf.Min(m_TimestampCount + 1, m_BufferedState.Length);
// Check integrity, lowest numbered state in the buffer is newest and so on
for (int i = 0; i < m_TimestampCount - 1; i++)
{
if (m_BufferedState[i].timestamp < m_BufferedState[i + 1].timestamp)
Debug.Log("State inconsistent");
}
}
}
// This only runs where the component is enabled, which is only on remote peers (server/clients)
void Update()
{
double currentTime = PhotonNetwork.time;
double interpolationTime = currentTime - interpolationBackTime;
// We have a window of interpolationBackTime where we basically play
// By having interpolationBackTime the average ping, you will usually use interpolation.
// And only if no more data arrives we will use extrapolation
// Use interpolation
// Check if latest state exceeds interpolation time, if this is the case then
// it is too old and extrapolation should be used
if (m_BufferedState[0].timestamp > interpolationTime)
{
for (int i = 0; i < m_TimestampCount; i++)
{
// Find the state which matches the interpolation time (time+0.1) or use last state
if (m_BufferedState[i].timestamp <= interpolationTime || i == m_TimestampCount - 1)
{
// The state one slot newer (<100ms) than the best playback state
State rhs = m_BufferedState[Mathf.Max(i - 1, 0)];
// The best playback state (closest to 100 ms old (default time))
State lhs = m_BufferedState[i];
// Use the time between the two slots to determine if interpolation is necessary
double length = rhs.timestamp - lhs.timestamp;
float t = 0.0F;
// As the time difference gets closer to 100 ms t gets closer to 1 in
// which case rhs is only used
if (length > 0.0001)
t = (float)((interpolationTime - lhs.timestamp) / length);
// if t=0 => lhs is used directly
transform.localPosition = Vector3.Lerp(lhs.pos, rhs.pos, t);
transform.localRotation = Quaternion.Slerp(lhs.rot, rhs.rot, t);
return;
}
}
}
// Use extrapolation. Here we do something really simple and just repeat the last
// received state. You can do clever stuff with predicting what should happen.
else
{
State latest = m_BufferedState[0];
transform.localPosition = Vector3.Lerp(transform.localPosition, latest.pos, Time.deltaTime * 20 );
transform.localRotation = latest.rot;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment