🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

How do i correctly interpolate between 2 positions recieved from server?

Started by
3 comments, last by hplus0603 2 years, 5 months ago

I am trying to implement interpolation in client side. I store Vector2 position data recieved from server in a list and try to interpolate (x,y) from previous position to next position as data comes. But i couldnt implement it correctly and its not smooth. Tried many differet ways, some was actually smooth by luck but i want to learn how to implement it in correct way. here i post an example code which is not smooth at all and has missing parts i guess.

Actually data comes from client1 as it moves his mouse which sends the coordinates to the server which just directs it to client2, so i dont know in which rate they are coming. I mean data is not coming in constant rate from server which is correct way to implement a server authorative game but anyway. if data must come in constant rate i can also implement it.

private final float STEP = 1.0f / 240f;
private float accumulator;
public static ArrayList<Vector2> ballPositionList = new ArrayList<>();


public void onPositionData(Vector2 positionVector){ // called when vector2 position comes from server
      ballPositionList.add(positionVector);
}

public void render(float delta) {
        accumulator += delta;
 while (accumulator >= STEP) {
            
            table.act(STEP);
            accumulator -= STEP;
        }
        table.interpolate(accumulator / STEP);
}

public void interpolate(float delta){ // tables interpolate method
if(ballPositionList.size() >1 ){
        while(ballPositionList.size() > 2){ // feel like i add another boolean check here but idk what should i
            ballPositionList.remove(0);


            Vector2 prevPosition = ballPositionList.get(0);
            Vector2 nextPosition = ballPositionList.get(1);
            float interpolationFactor = delta; // this is wrong. what should it be?
            float x = nextPosition.x * interpolationFactor  + prevPosition.x * (1.0f - interpolationFactor );
            float y = nextPosition.y * interpolationFactor  +  prevPosition.y * (1.0f - interpolationFactor );

            getBall().set(x, y);


        }
    }


}
Advertisement

You have to either run the other player “behind time” and interpolate, or run the other player “forward predicted” and extrapolate. It looks like you want to do the first of those.

Keep an estimate of the delta between server time and your local time. When you receive a new update from the server, it should be timestamped with when it was sent. Make your clock offset be approximately the transmission delay from server to you, plus a small margin.

You will now have a table of received times and positions:

time    position
3.114   12.0, 14.0
3.225   8.0, 13.0
3.318   10.0, 12.0
3.420   11.0, 12.0

The times may be more regular if you use a fixed time step and send tick numbers; that's fine.

Now, given your current local time when it's time to render, subtract your server offset, and look up the time in question in the table. Let's say your estimated server time with offset tS is 3.299, you will find your two times t1=3.225 and t2=3.318 with their corresponding positions p1 and p2

The position you want to render at, is then (tS-t1)/(t2-t1)*(p2-p1)+p1

You can also note that the earliest value in your table, 3.114, will never be needed again, unless time goes backwards, so you can remove it from the table. In practice, a table like this seldom needs more than 3 elements.

When you find that you never reach the “latest” slot in the table when rendering, you can reduce your estimate of the server offset, so you render “later” times.

When you find that you want to render a time that's later than what's available in the table, you can infer that the server is further away than you thought, and you can increase the estimated offset.

enum Bool { True, False, FileNotFound };

@hplus0603 so client1 sends position to server, before server directs position data to client2 does it add timestamp ? do you mean System.miliseconds or something else as time ?

(im not doing server authorative as i said for this example)

Server can timestamp on its own if it wants, or the client can send the timestamp it intends for the message to happen at (which of course allows more cheating.)

What particular value you use for “time” depends on your game design. Some systems use milliseconds, some systems use “tick number,” and some systems use more advanced systems like vector clocks (I highly recommend you don't go that way until you're very used to networked programming.)

The most important part is that all the clients maintain an offset estimate to the server, so the time stamps are intended to be interpreted in the same frame of reference. Reading a clock value locally is unlikely to be at all synchronized with the value some other computer is reading, although the clocks can be assumed to advance at almost exactly the same rate (as in: “one second per second.”)

enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement