Multiplayer Peer-To-Peer position interpolation I have coded this movement interpolation for Unity3d using Photon. 

OnPhotonSerializeView gets called at the tickrate.

Any kind of criticism/improvement suggestions are welcome. I tried to make the code as plain as possible because I don't see this as something that should be over-engineered because it is rather algorithmic in nature.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerInterpolation : MonoBehaviour {
    Queue&lt;Vector3&gt; positions = new Queue&lt;Vector3&gt;();
    Queue&lt;Quaternion&gt; rotations = new Queue&lt;Quaternion&gt;();
    Queue&lt;int&gt; frameNumbers = new Queue&lt;int&gt;();
    static int amountBuffered = 1;
    static float oneTickTime = 1.0f / Settings.tickRate;

    PhotonView photonView;
    int localSentFrameNumber = 0;
    int lastReceivedFrameNumber = -1;
    int lastFrameNumberMovedTowards = 0;

    int expectedExecuteFrameNumber = 0; //An estimation of what frame number should be executing at this time.
    float expectedExecuteFrameNumberLastExecutedTime = 0;

    Vector3 moveTowards;
    float moveTowardsSpeed = 1.0f;
    bool moveInProgress = false;

    void Start() {
        photonView = gameObject.GetComponent&lt;PhotonView&gt;();
        moveTowards = transform.position;
    }

    void Update() {
        if (photonView.isMine) return;

        transform.position = Vector3.MoveTowards(transform.position, moveTowards, moveTowardsSpeed * Time.deltaTime);
        if (Mathf.Abs(Vector3.Distance(transform.position, moveTowards)) &lt; 0.001f) {
            moveInProgress = false;
        } else {
            moveInProgress = true;
        }

        if (expectedExecuteFrameNumberLastExecutedTime + oneTickTime &lt; Time.realtimeSinceStartup) {
            expectedExecuteFrameNumber++;
            expectedExecuteFrameNumberLastExecutedTime = Time.realtimeSinceStartup;
        }

        if (frameNumbers.Count &gt; 0 &amp;&amp; ((frameNumbers.Peek() + amountBuffered &lt;= lastReceivedFrameNumber) || frameNumbers.Peek() &lt;= expectedExecuteFrameNumber - amountBuffered)) {
            if (lastReceivedFrameNumber - frameNumbers.Peek() &gt; amountBuffered) {
                int trimUntil = lastReceivedFrameNumber - amountBuffered;
                while (frameNumbers.Peek() &lt; trimUntil) {
                    positions.Dequeue();
                    rotations.Dequeue();
                    frameNumbers.Dequeue();
                }
            }

            Vector3 position = positions.Dequeue();
            Quaternion rotation = rotations.Dequeue();
            int frameNumber = frameNumbers.Dequeue();

            if (moveInProgress == true) {
                transform.position = moveTowards;
            }

            float distance = Vector3.Distance(transform.position, position);
            moveTowards = position;
            moveTowardsSpeed = distance * (frameNumber - lastFrameNumberMovedTowards) / oneTickTime;

            lastFrameNumberMovedTowards = frameNumber;

            expectedExecuteFrameNumber = frameNumber;
            expectedExecuteFrameNumberLastExecutedTime = Time.realtimeSinceStartup;
        }
    }

    void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) {
        if (stream.isWriting) {
            stream.SendNext(transform.position);
            stream.SendNext(transform.rotation);
            stream.SendNext(localSentFrameNumber++);
        } else {
            positions.Enqueue((Vector3)stream.ReceiveNext());
            rotations.Enqueue((Quaternion)stream.ReceiveNext());
            int frameNumber = (int)stream.ReceiveNext();
            frameNumbers.Enqueue(frameNumber);
            lastReceivedFrameNumber = frameNumber;
        }
    }
}
I recommend you always write access modifiers for class members and for classes itself. It will make code more clear and understandable. So instead of


Queue&lt;Vector3&gt; positions = new Queue&lt;Vector3&gt;();
Queue&lt;Quaternion&gt; rotations = new Queue&lt;Quaternion&gt;();
...
void Start() { ... }



write

private Queue&lt;Vector3&gt; positions = new Queue&lt;Vector3&gt;();
private Queue&lt;Quaternion&gt; rotations = new Queue&lt;Quaternion&gt;();
...

private void Start() { ... }


Also these queues are not supposed to be changed so mark them with readonly keyword:

private readonly Queue&lt;Vector3&gt; positions = new Queue&lt;Vector3&gt;();
...




It is a matter of taste but it is more common for C# to place opening curly braces on the next line:

private void Start()
{
    photonView = gameObject.GetComponent&lt;PhotonView&gt;();
    moveTowards = transform.position;
}




This code 


if (Mathf.Abs(Vector3.Distance(transform.position, moveTowards)) &lt; 0.001f) {
    moveInProgress = false;
} else {
    moveInProgress = true;
}



can be simplified to

moveInProgress = Mathf.Abs(Vector3.Distance(transform.position, moveTowards)) &gt;= 0.001f;


Also it would be better to declare 0.001f as named constant like

private const float DistanceEpsilon = 0.001f;




You should definitely rewrite these conditions:


if (frameNumbers.Count &gt; 0 &amp;&amp; ((frameNumbers.Peek() + amountBuffered &lt;= lastReceivedFrameNumber) || frameNumbers.Peek() &lt;= expectedExecuteFrameNumber - amountBuffered)) {
    if (lastReceivedFrameNumber - frameNumbers.Peek() &gt; amountBuffered)



First of all, you use frameNumbers.Peek() multiple times here so store the result of it to variable:

var firstFrameNumber = frameNumbers.Peek();


I recommend to use Any method for checking a collection on emptiness:

frameNumbers.Any()


instead of


frameNumbers.Count &gt; 0



Thanks @Nikita B for pointing out that frameNumbers.Any() should be done before frameNumbers.Peek(). In your case you can just return from the method if there are no elements in the frameNumbers:

if (!frameNumbers.Any())
    return;


These conditions


frameNumbers.Peek() + amountBuffered &lt;= lastReceivedFrameNumber
lastReceivedFrameNumber - frameNumbers.Peek() &gt; amountBuffered



can be written in one form:

lastReceivedFrameNumber - frameNumbers.Peek() &gt; amountBuffered
lastReceivedFrameNumber - frameNumbers.Peek() &gt;= amountBuffered


Now we see that we can store lastReceivedFrameNumber - frameNumbers.Peek() to variable since this expression is used multiple times. So the final form of conditions above will be

if (!frameNumbers.Any())
    return;

var firstFrameNumber = frameNumbers.Peek();
var frameNumbersDifference = lastReceivedFrameNumber - firstFrameNumber;

if (frameNumbersDifference &gt;= amountBuffered ||
    firstFrameNumber &lt;= expectedExecuteFrameNumber - amountBuffered)
{
    if (frameNumbersDifference &gt; amountBuffered)


Also I recommend to extract all subconditions to methods with appropriate names. It will make your code much more easy to read and understand.



Small note: you don't need to write == true or == false when checking value of bool variable. This


if (moveInProgress == true) {



should be rewritten as

if (moveInProgress)
{