The easiest way to implement interpolation correctly is by using fixed time steps. Basically, if you know you're going to get a new position about 10 times a second, you can interpolate from the last position to the current one in the time it takes to get a new position (.1 seconds).
In reality, there are things like jitter that make your job harder so packets won't always arrive .1 seconds apart if they're sent .1 seconds apart. This problem can be solved with some buffering. Instead of immediately accepting a position as the current position when you receive it, wait until the next expected sync frame to recognize it.
//When data is received, store it in the buffer
void OnReceiveData (byte RawData)
BufferPos = DeserializationMagic (RawData);
//Runs every .1 seconds or however frequent this character gets synced
void FixedUpdate ()
LastPos = CurrentPos;
CurrentPos = BufferPos;
ElapsedTime = 0;
//Send out your position to everyone (except yourself)
//Used for smooth interpolation
//Interpolate position every frame based on time since last position was set
void Update ()
ElapsedTime += Time.deltaTime;
//Lerps by a factor of 10 since interpolation needs to happen within .1 seconds
transform.position = Vector3.Lerp (LastPos, CurrentPos, ElapsedTime * 10);
There are a bunch of things you can do with time stamps and whatnot for staggered syncing, but at 10 syncs per second, any advantages you get without an expected sync frame probably won't get noticed. Plus, this is the most straightforward and simple way to get smooth interpolation in my opinion.
I don't recommend using RPCs to sync objects though, since the RPC most likely is reliable. Reliable protocols involve sending an ACK packet which means another 20-40 bytes for the packet header. The sender could also resend the original packet if an ACK was not received in time.
Syncing unreliably should be fine since if 1 or 2 packets go missing, the next received packet will fill in the gap.