Interpolating player updates

So right now I’m serializing the player movements about 20 times a second

I read that Source uses an interpolation period of 100 milliseconds, so I tried:

transform.localPosition = Vector3.Lerp(oldPos, newPos,0.1); I was previously doing this by transferring velocity but over time it becomes a bit inaccurate so i’d like to use Lerps

Obviously this is on the observing end. newPos is the most recent position packet recieved, and oldPos is the previous packet received. oldPos and newPos are both correct, I’ve debugged quite a bit there.

Am I doing this right? This is in the main Update() function and I’ve tried 0.1, 50, 1, 0.5, 0.8 and I see no visual difference

What may be happening is I’m re- assigning oldPos/newPos so fast that the lerp can’t keep up.

Should I store them in an array and only move them from point to point when they arrive at their destination, rather than constantly re-assigning oldPos and newPos while the lerp is using them?

Have a look at this script:
http://dorumon.googlecode.com/svn/trunk/Networking/Assets/Authoritative%20Server/GraduallyUpdateState.cs

Interesting, thanks for that

I tried making my own script side by side to that one and it didn’t seem to work right so ill have to try it again sometime.

Not too thrilled about this, it was a last minute change I decided to do to movement and id prefer to be working on other things rather than fiddling with this right now. Is there a quick “good enough” way I can just smooth the movement as a temporary solution? Prediction based on velocity was working quite well other than that it would de sync enough to cause a very tiny stutter every now and then but it definitely wasn’t a consistent enough hick to inhibit gameplay.

I had this working somehow before you replied, but for some reason its stopped working, but im pretty sure it may not have worked over a network because of ping times anyway

private var duration:float = 0.1;
function Update() {
if(transform.localPosition != mov.newPos) {
t += Time.deltaTime / duration;
transform.localPosition = Vector3.Lerp(mov.oldPos, mov.newPos,t);
} else {
t = 0;
}

I just need to stop the mini teleporting

For a small/quick solution, consider something like this:

using UnityEngine;
using System.Collections;

//
// Positions an object based on a network tick.
//
public class NetworkPositionedObject : MonoBehaviour {

    private Vector3 _oldPosition;
    private Vector3 _newPosition;
    private float _stateTime;

    private float _networkTickTime = 0.1f;

    public Vector3 OldPosition
    {
        get { return _oldPosition; }
    }

    public Vector3 NewPosition
    {
        get { return _newPosition; }
        set
        {
            if (_newPosition != value)
            {
                _oldPosition = _newPosition;
                _newPosition = value;
                _stateTime = Time.time;
            }
        }
    }

    void Awake()
    {
        base.Awake();
        _oldPosition = transform.position;
        _newPosition = _oldPosition;
    }

    void Update()
    {
        if (transform.position == NewPosition)
        {
            return;
        }

        // Interpolate position based on time between ticks.
        float t = (Time.time - _stateTime) / _networkTickTime;
        transform.position = _oldPosition + ((_newPosition - _oldPosition) * t);
    }
}

Your code looks interesting, let me know if you find a working version when you get home

The version I have above now should work. I just had to remove a line I was using for testing. The line I removed was an attempt to allow external scripts to modify transform.position directly rather than NewPosition, but it wasn’t working.

To move an object, you set it’s NewPosition property, and then it will take care of interpolating to that position.

I had to modify your script a tiny bit but even before I modified it the player does not appear for other clients:

This is how I’m setting newPosition in another file:

if (stream.isWriting) {
newPos = transform.localPosition;
stream.Serialize(newPos);
} else {
stream.Serialize(newPos);
GetComponent(“NetworkPositionedObject”)._newPosition = newPos;

and here is the slightly modified version of your script:

using UnityEngine;

using System.Collections;

 

//

// Positions an object based on a network tick.

//

public class NetworkPositionedObject : MonoBehaviour {

 

    private Vector3 _oldPosition;

    private Vector3 _newPosition;

    private float _stateTime;

 

    private float _networkTickTime = 0.1f;

 

    public Vector3 OldPosition

    {

        get { return _oldPosition; }

    }

 

    public Vector3 NewPosition

    {

        get { return _newPosition; }

        set

        {

            if (_newPosition != value)

            {

                _oldPosition = _newPosition;

                _newPosition = value;

                _stateTime = Time.time;

            }

        }

    }

 

    void Awake()

    {

//        base.Awake(); got an error here for some reason, I use JS not C# so I wasn't sure why

        _oldPosition = transform.localPosition; // entire game takes place on a giant moving platform so I have to use localposition, shouldn't be an issue, they always have a parent

        _newPosition = _oldPosition;

    }

 

    void Update()

    {
		if(!networkView.isMine) { // only interpolate entities I don't own

        if (transform.localPosition == NewPosition)

        {

            return;

        }

 

        // Interpolate position based on time between ticks.

        float t = (Time.time - _stateTime) / _networkTickTime;

        transform.localPosition = _oldPosition + ((_newPosition - _oldPosition) * t);
			
		}
    }

}

You don’t want to set _newPosition directly. Instead, use the NewPosition setter. This allows it to update the old position and state time.

How exactly would I do that? And is it possible for me to do it from a JS file?

I see public Vector3 NewPosition but I don’t see any parameter there to set. Like I said I really don’t know much regarding C# haha

Oh I see, I can do NewPosition =

This works really well! Only issue is when I stop moving the player continues to slowly be drifting/interpolating somewhere, but other than that it really cleans up the movement, thanks so much!

That drift seemed to resolve the second time I tested it, it only happened right after the player spawned and was very subtle.

Remember that you can lose packages so you should sent time and measure time between packages retrieved.

So if you lose a lot of packages you need to use extrapolation instead.

Is a good method of extrapolation to re-apply the same direction from the most recent packet * the same current speed until I get another valid packet?

Isn’t that the same as just applying the same velocity or am I mistaken

I’m glad it’s at least helping a bit. This is just something I whipped together for some networking tests, but it hasn’t been battle-tested yet!

As for extrapolation, using the same direction/velocity of the last known update is good. However, you should only extrapolate for a short period of time (maybe 250 ms). If you continue extrapolating past that, your predictions are more and more likely to be incorrect.

Surely 250ms is enough to recover a valid packet or two, if they’re coming in at 50ms intervals

I also battle tested your script just now and it seems to hold up quite well :slight_smile: no major hicks or issues, enough to keep the game playable at all times. I had someone test from Wisconsin while I’m in Florida (US) and he was moving quite well, he said I was also moving very smooth

So I have a few more questions

If I use reliable compressed on the NetworkView, wouldn’t that prevent packet loss? Is there any major downside to this? I’m guessing the buffered packets would cause more lag to that client as they build up? And the overhead for confirming delivery would use up more bandwidth per player?

And now a question about Source, is their position buffering and 50ms delay on all player movement a cushion for packet loss? It allows packets to be dropped without much visual hick, and then finally if too many packets are lost it switches to interpolation. I’d assume all their efforts regarding packet loss was because CSS/CS1.6 are very old games, would you say nowadays networks are a bit more reliable?

It usually is, but connectivity issues can be pretty unforgiving at times, so you ideally should gracefully handle it.

Reliable channels don’t prevent packet loss. Packet loss will always occur. Reliability means that if the packet is lost, it will be resent. A resent packet will arrive much later than the original was supposed to.
Reliability only ensures it will eventually get there.

I’m not totally sure if Source does it for packet loss or not. I believe their delay is so that you always have the next known position that you can interpolate to. Otherwise, if you’re always at the latest known spot, you’ll have to wait for a new update before you can get moving again. Of course, you can do prediction here, but it’s not always very accurate.

Well in that case theres nothing like a good ol’ teleport update that multiplayer is known for, sometimes you just gotta let it happen haha

That makes sense. Really appreciate all the info and help sballew7

You should base your new possition based on the last time you retrived a package. So something like your current NetworkTime - MaxInterpolationTime - lastPackage.Time.

Then multiply your last velocity with the variable you just calculated and then add it to your last position.

Hey sballew7 I sent you a pm about your script continuing to interpolate even if the player isn’t moving (they’ll slowly drift off for some reason) not sure why and its harder for me to read being c#