Interpolation- beginner networking question

I’m currently evaluating Unity Pro, and potentially looking to move my team to it for future developments. So far, everything has been wonderfully smooth and straightforward.

I’ve just implemented networking, which seems almost too simple to be true. My network players simply call NetworkInstantiate, and they appear in the world, and move around correctly.

Almost correctly.

For some reason, there appears to be no interpolation whatsoever - the objects teleport around the world at about 5 frames a second.

Is there an obvious ‘interpolation on’ switch I’m missing here?

Thanks in advance!

There is code for a NetworkRigidBody script (don’t remember where I got it), put this code into a script, attach it to your gameobject, set the NetworkView observed property to point to this script in the inspector:

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<m_TimestampCount-1;i++)
   {
    if (m_BufferedState[i].timestamp < m_BufferedState[i+1].timestamp)
     Debug.Log("State inconsistent");
   } 
  }
  }
 // 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 extra polation
 void Update () {
  // This is the target playback time of the rigid body
  double interpolationTime = Network.time - m_InterpolationBackTime;
   
  // Use interpolation if the target playback time is present in the buffer
  if (m_BufferedState[0].timestamp > interpolationTime)
  {
			// Go through buffer and find correct state to play back
   for (int i=0;i<m_TimestampCount;i++)
   {
    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
     // Example:
     // Time is 10.000, so sampleTime is 9.900 
     // lhs.time is 9.910 rhs.time is 9.980 length is 0.070
     // t is 9.900 - 9.910 / 0.070 = 0.14. So it uses 14% of rhs, 86% of lhs
     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
  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;
   }
  }
  }
}

Add it to any gameobject with a rigidbody to do interpolation/extrapolation on it.

That works perfectly, thank you SO much!