Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Third Party NetworkInterpolatedTransform to Photon Unity Networking

Discussion in 'Multiplayer' started by Azaldur, Oct 11, 2012.

  1. Azaldur

    Azaldur

    Joined:
    Jul 10, 2011
    Posts:
    52
    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 .

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class NetworkInterpolatedTransform : Photon.MonoBehaviour {
    5.  
    6.     public double interpolationBackTime = 0.1;
    7.  
    8.     internal struct State
    9.     {
    10.         internal double timestamp;
    11.         internal Vector3 pos;
    12.         internal Quaternion rot;
    13.     }
    14.  
    15.     // We store twenty states with "playback" information
    16.     State[] m_BufferedState = new State[20];
    17.  
    18.     // Keep track of what slots are used
    19.     int m_TimestampCount;
    20.  
    21.     void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    22.     {
    23.         // Always send transform (depending on reliability of the network view)
    24.         if (stream.isWriting)
    25.         {
    26.             Vector3 pos = transform.localPosition;
    27.             Quaternion rot = transform.localRotation;
    28.             stream.Serialize(ref pos);
    29.             stream.Serialize(ref rot);
    30.         }
    31.         // When receiving, buffer the information
    32.         else
    33.         {
    34.             // Receive latest state information
    35.             Vector3 pos = transform.position;
    36.             Quaternion rot = Quaternion.identity;
    37.             stream.Serialize(ref pos);
    38.             stream.Serialize(ref rot);
    39.  
    40.             // Shift buffer contents, oldest data erased, 18 becomes 19, ... , 0 becomes 1
    41.             for (int i = m_BufferedState.Length-1; i >= 1; i--)
    42.             {
    43.                 m_BufferedState[i] = m_BufferedState[i-1];
    44.             }
    45.  
    46.             // Save currect received state as 0 in the buffer, safe to overwrite after shifting
    47.             State state;
    48.             state.timestamp = info.timestamp;
    49.             state.pos = pos;
    50.             state.rot = rot;
    51.             m_BufferedState[0] = state;
    52.  
    53.             // Increment state count but never exceed buffer size
    54.             m_TimestampCount = Mathf.Min(m_TimestampCount + 1, m_BufferedState.Length);
    55.  
    56.             // Check integrity, lowest numbered state in the buffer is newest and so on
    57.             for (int i = 0; i < m_TimestampCount-1; i++)
    58.             {
    59.                 if (m_BufferedState[i].timestamp < m_BufferedState[i+1].timestamp)
    60.                 Debug.Log("State inconsistent");
    61.             }
    62.  
    63.             //Debug.Log("stamp: " + info.timestamp + "my time: " + PhotonNetwork.time + "delta: " + (PhotonNetwork.time - info.timestamp));
    64.         }
    65.     }
    66.  
    67.     // This only runs where the component is enabled, which is only on remote peers (server/clients)
    68.     void Update ()
    69.     {
    70.         double currentTime = PhotonNetwork.time;
    71.         double interpolationTime = currentTime - interpolationBackTime;
    72.         // We have a window of interpolationBackTime where we basically play
    73.         // By having interpolationBackTime the average ping, you will usually use interpolation.
    74.         // And only if no more data arrives we will use extrapolation
    75.  
    76.         // Use interpolation
    77.         // Check if latest state exceeds interpolation time, if this is the case then
    78.         // it is too old and extrapolation should be used
    79.         if (m_BufferedState[0].timestamp > interpolationTime)
    80.         {
    81.             for (int i = 0; i < m_TimestampCount; i++)
    82.             {
    83.                 // Find the state which matches the interpolation time (time+0.1) or use last state
    84.                 if (m_BufferedState[i].timestamp <= interpolationTime || i == m_TimestampCount-1)
    85.                 {
    86.                     // The state one slot newer (<100ms) than the best playback state
    87.                     State rhs = m_BufferedState[Mathf.Max(i-1, 0)];
    88.  
    89.                     // The best playback state (closest to 100 ms old (default time))
    90.                     State lhs = m_BufferedState[i];
    91.  
    92.                     // Use the time between the two slots to determine if interpolation is necessary
    93.                     double length = rhs.timestamp - lhs.timestamp;
    94.                     float t = 0.0F;
    95.                     // As the time difference gets closer to 100 ms t gets closer to 1 in
    96.                     // which case rhs is only used
    97.                     if (length > 0.0001)
    98.                         t = (float)((interpolationTime - lhs.timestamp) / length);
    99.  
    100.                     // if t=0 => lhs is used directly
    101.                     transform.localPosition = Vector3.Lerp(lhs.pos, rhs.pos, t);
    102.                     transform.localRotation = Quaternion.Slerp(lhs.rot, rhs.rot, t);
    103.                     return;
    104.                 }
    105.             }
    106.         }
    107.         // Use extrapolation. Here we do something really simple and just repeat the last
    108.         // received state. You can do clever stuff with predicting what should happen.
    109.         else
    110.         {
    111.             State latest = m_BufferedState[0];
    112.             transform.localPosition = latest.pos;
    113.             transform.localRotation = latest.rot;
    114.         }
    115.     }
    116. }
     
  2. gsus725

    gsus725

    Joined:
    Aug 23, 2010
    Posts:
    250
    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
     
  3. Azaldur

    Azaldur

    Joined:
    Jul 10, 2011
    Posts:
    52
    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 :(?
     
  4. appels

    appels

    Joined:
    Jun 25, 2010
    Posts:
    2,687
    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.
     
  5. Azaldur

    Azaldur

    Joined:
    Jul 10, 2011
    Posts:
    52
    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?
     
  6. appels

    appels

    Joined:
    Jun 25, 2010
    Posts:
    2,687
    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.
     
  7. Azaldur

    Azaldur

    Joined:
    Jul 10, 2011
    Posts:
    52
    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.
     
  8. appels

    appels

    Joined:
    Jun 25, 2010
    Posts:
    2,687
    The other script is called GraduallyUpdate or something and is included in the networking sample
     
  9. Paradoks

    Paradoks

    Joined:
    Oct 13, 2009
    Posts:
    436
    Azaldur, did you found a solution ?
     
  10. DBarlok

    DBarlok

    Joined:
    Apr 24, 2013
    Posts:
    268
    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! :D
     
    Last edited: Jun 6, 2013
  11. danish115

    danish115

    Joined:
    Aug 28, 2014
    Posts:
    47
    what i want to do that to break my keyboard on my lcd. Photon it just F***ed 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 ??