interpolation using buffer

Options
Hi,

I have *ported* a Unity Network C-Sharp Script to Photon and have some issues.
First of all this is the code I use - the player object has a PhotonView component that is observing this script.

[code2=csharp]using UnityEngine;
using System.Collections;

public class NetworkedPlayer : Photon.MonoBehaviour
{
// how far back to rewind interpolation?
public float InterpolationBackTime = 0.1f;

// a snapshot of values received over the network
private struct networkState
{
public Vector3 Position;
public double Timestamp;

public networkState( Vector3 pos, double time )
{
this.Position = pos;
this.Timestamp = time;
}
}

// we'll keep a buffer of 20 states
networkState[] stateBuffer = new networkState[ 20 ];
int stateCount = 0; // how many states have been recorded

void Update()
{
if( photonView.isMine == true ) return; // don't run interpolation on the local object
if( stateCount == 0 ) return; // no states to interpolate

double currentTime = Network.time;
double interpolationTime = currentTime - InterpolationBackTime;

// the latest packet is newer than interpolation time - we have enough packets to interpolate
if( stateBuffer[ 0 ].Timestamp > interpolationTime )
{
for( int i = 0; i < stateCount; i++ )
{
// find the closest state that matches network time, or use oldest state
if( stateBuffer[ i ].Timestamp <= interpolationTime || i == stateCount - 1 )
{
// the state closest to network time
networkState lhs = stateBuffer[ i ];

// the state one slot newer
networkState rhs = stateBuffer[ Mathf.Max( i - 1, 0 ) ];

// use time between lhs and rhs to interpolate
double length = rhs.Timestamp - lhs.Timestamp;
float t = 0f;
if( length > 0.0001 )
{
t = (float)( ( interpolationTime - lhs.Timestamp ) / length );
}

transform.position = Vector3.Lerp( lhs.Position, rhs.Position, t );
break;
}
}
}
}

void OnPhotonSerializeView( PhotonStream stream, PhotonMessageInfo info )
{
Vector3 position = Vector3.zero;
if( stream.isWriting )
{
position = transform.position;
stream.Serialize( ref position );
}
else
{
stream.Serialize( ref position );
bufferState( new networkState( position, info.timestamp ) );
}
}

// save new state to buffer
void bufferState( networkState state )
{
// shift buffer contents to accomodate new state
for( int i = stateBuffer.Length - 1; i > 0; i-- )
{
stateBuffer[ i ] = stateBuffer[ i - 1 ];
}

// save state to slot 0
stateBuffer[ 0 ] = state;

// increment state count (up to buffer size)
stateCount = Mathf.Min( stateCount + 1, stateBuffer.Length );
}
}[/code2]

It works perfectly fine in Unity´s built-in solution. Does anyone know why this does not seem to work with Photon Cloud?

Regards,

CineTek

Comments

  • Tobias
    Options
    In which way does it fail?
  • The player´s position does not seem to be updated at all before the buffer reaches 20 units. And afterwards it is extremely laagy, far away from a lerp or smooth movement. Also if the player stops moving then the actual position does not get transmitted -> the player is standing at an outdated position. The Script seems to stop going through the saved buffer states. But again, this works fine with Unity´s built-in solution.


    Edit: I am using the latest Unity 4.3.1 version with the free plan. My current project is a 2.5D Beat´em Up/Brawler which relies on a great network synchronization. This method/script seemed to be the best way for nearly 100% accurate movement synchronization which will be even more important when it comes to hitting enemies or dodging attacks. If there is any better method feel free to post your ideas. It´s just that I have tried several options before including the rigidbody/transform-script and these did not gave me acceptable results so far.

    Edit2: I found the bug that was causing this weird behaviour: It is in line 40! It should be this:

    if( stateBuffer[ i ].Timestamp >= interpolationTime || i == stateCount - 1 )

    Another thing I noticed is that PhotonNetwork.sendRate and PhotonNetwork.sendRateOnSerialize are not equal. I don´t know if this is on purpose but I changed the second variable to be equal to 20 and now everything seems to be working way smoother than before. I have heard that a high sendRate does not have to mean that each packet gets called per frame. But I hope that I can optimize other netcode so that there is no problem with that ;)
  • Tobias
    Options
    Good to read you found the bug!

    The sendRateOnSerialize defines how many times per second OnPhotonSerializeView is called (if you use that). The sendRate simply defines how often we check the outgoing queues and send whatever is there in a UPD package (actually making the data travel).
    Those 2 times are not the same thing exactly.
    They are not synced so to avoid extra lag when OnPhotonSerializeView was just called, we also send a (extra) package. The sendRate more or less only affects RPCs and other stuff.

    You should experiment with the sendRateOnSerialize. Maybe you can go a bot lower than 20. Definitely, if you send this often, you should use "Unreliable" updates and only send RPCs reliably.
  • impossible
    Options
    I've ported this, plus rigidbody sync, into one single component that you set up in the inspector: https://dl.dropboxusercontent.com/u/469 ... itypackage

    This component works for players and rigidbodies, and includes interpolation and extrapolation.
  • Thank you very much!!!

    This gives my a very helpful example of these different methods and how to use them to improve my netcode :)

    The CubeExtra and CubeTransform examples seem to give me the best experience but I will try the other ones as well.

    I would suggest to put this into the PUN examples provided by Asset Store.

    Thanks,

    CineTek
  • Tobias
    Options
    Impossible: Thanks for posting that package. Much appreciated.
    I will take a look when things here calm down a bit and we finally can update the samples again.