NetworkInterpolatedTransform to Photon Unity Networking

Hi,
I transfered the NetworkInterpolatedTransform.cs, which works perfectly fine with unitys build in networking, of the Unity Network Example to PUN. But, in contrast to unity networking, the movement of the remote players is far from smooth. I get a much better result with a simple lerp from the actual position to the new received position. With a interpolationBackTime of 0.1(default Time) the remote players just jump from one position to the next. When i set the default Time to 0.2 or higher, the movement gets smoother(but far away from a simple lerp), but the received data is also more outdated. I use Photon Cloud. Please let me know, if you figure out what my mystake is, that would be great .

using UnityEngine;
using System.Collections;
 
public class NetworkInterpolatedTransform : Photon.MonoBehaviour {
 
    public double interpolationBackTime = 0.1; 
 
    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 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 = transform.position;
            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");
            }
 
            //Debug.Log("stamp: " + info.timestamp + "my time: " + PhotonNetwork.time + "delta: " + (PhotonNetwork.time - info.timestamp));
        }
    }
 
    // 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 = latest.pos;
            transform.localRotation = latest.rot;
        }
    }
}

I know I’m not really helping but this script seems like glorified lerping I don’t really see the difference except that this takes more code. But I’m no pro

Hi gsus, there is a difference, I think its a simple script for entity interpolation. Does someone got an idea why this doesn’t work with photon cloud :(?

I don’t see any reason why it shouldn’t work. All this script does is lerp between 2 buffered states ( the last one and the one before ). So if the buffer is filled with data it receives from Photon server, it should work. If it doesn’t, then at least you should debug the script and see if the buffer contains data. And if it does, why the lerping doesn’t occur.

Hi appels,
thanks for your answer :). Yeah, i also have no idea why it doesn’t work, thats why im asking here :/. I did some further testing. The buffer always contains data, but I realized that while in the network example interpolation is always used when the remote player is moving, with photon it switches between extrapolation and interpolation. I also host a photon server instead of using cloud, now. So there shouldn’t be any ping differences to unitys networking solution. If interpolation or extrapolation is used is decided by this line: if (m_BufferedState[0].timestamp > interpolationTime){// interpolation} else {//extrapolation}. Does that help someone to find a soultion?

Wrong → different application, different results.

Thats what it’s supposed to do.
interpolation when there is data in the buffer, extrapolation when there isn’t.
So from what your saying, some things are working.
Maybe there is an issue with the lerping ?
Also notice that there is a huge difference between this script and the one from the network sample.
In the original one you lerp from a local buffered state to a received state from the server… which is not used in this script.

Hi appels,
can you please post the script you got? Because in the network example I got on my computer, the script is exactly the same.

The other script is called GraduallyUpdate or something and is included in the networking sample

Azaldur, did you found a solution ?

Appels:

You don’t know how much are you helping me out in my quest for trying understanding multiplayer networking. Im not english native but your explanation is clear as water. (it cant take up to 4 hours trying to understand one simple thing in a foreign languaje)

Thank you so much for the interpolation / extrapolation explain. I will research further, cause im having the “jittering” issue with Photon too.

Ehm, thanks! :smile:

what i want to do that to break my keyboard on my lcd. Photon it just fucked me. there is no video demo or any detailed thing which actually help me. everybody just tell me what to do in words but there is no implementation detail. what the heck is it ??