using UnityEngine; using System.Collections; public class NetworkRigidbody : MonoBehaviour { public double m_InterpolationBackTime = 0.1; public double m_ExtrapolationLimit = 0.5; internal struct State { internal double timestamp; internal Vector3 pos; internal Vector3 velocity; internal Quaternion rot; internal Vector3 angularVelocity; } // We store twenty states with "playback" information State[] m_BufferedState = new State[20]; // Keep track of what slots are used int m_TimestampCount; void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info) { // Send data to server if (stream.isWriting) { Vector3 pos = rigidbody.position; Quaternion rot = rigidbody.rotation; Vector3 velocity = rigidbody.velocity; Vector3 angularVelocity = rigidbody.angularVelocity; stream.Serialize(ref pos); stream.Serialize(ref velocity); stream.Serialize(ref rot); stream.Serialize(ref angularVelocity); } // Read data from remote client else { Vector3 pos = Vector3.zero; Vector3 velocity = Vector3.zero; Quaternion rot = Quaternion.identity; Vector3 angularVelocity = Vector3.zero; stream.Serialize(ref pos); stream.Serialize(ref velocity); stream.Serialize(ref rot); stream.Serialize(ref angularVelocity); // Shift the buffer sideways, deleting state 20 for (int i=m_BufferedState.Length-1;i>=1;i--) { m_BufferedState[i] = m_BufferedState[i-1]; } // Record current state in slot 0 State state; state.timestamp = info.timestamp; state.pos = pos; state.velocity = velocity; state.rot = rot; state.angularVelocity = angularVelocity; m_BufferedState[0] = state; // Update used slot count, however never exceed the buffer size // Slots aren't actually freed so this just makes sure the buffer is // filled up and that uninitalized slots aren't used. m_TimestampCount = Mathf.Min(m_TimestampCount + 1, m_BufferedState.Length); // Check if states are in order, if it is inconsistent you could reshuffel or // drop the out-of-order state. Nothing is done here for (int i=0;i interpolationTime) { // Go through buffer and find correct state to play back for (int i=0;i 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 else { State latest = m_BufferedState[0]; float extrapolationLength = (float)(interpolationTime - latest.timestamp); // Don't extrapolation for more than 500 ms, you would need to do that carefully if (extrapolationLength < m_ExtrapolationLimit) { float axisLength = extrapolationLength * latest.angularVelocity.magnitude * Mathf.Rad2Deg; Quaternion angularRotation = Quaternion.AngleAxis(axisLength, latest.angularVelocity); rigidbody.position = latest.pos + latest.velocity * extrapolationLength; rigidbody.rotation = angularRotation * latest.rot; rigidbody.velocity = latest.velocity; rigidbody.angularVelocity = latest.angularVelocity; } } } }