Created
November 22, 2013 02:41
-
-
Save cnsoft/7593906 to your computer and use it in GitHub Desktop.
> CoreTech 这部分应该才是核心的点. 插槽式同步. 客户端预测.服务器端纠正. >发送过来的都进缓冲.带时间戳. 本地更新时 依次取有效缓冲点,做Lerp 插值. 延迟太高的话, 服务器发过来的会直接把一些过时的插槽去掉. 客户端更新时依旧是Lerp. 可以拉回到某位置.不会超出太多.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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