Looking for help with smooth multiplayer interpolated movement

@MackeyK24 @Chisato_Sozo do either of you see any issues with using an approach like this?

The correction vector shouldn’t be too large if you’re receiving regular updates at 50ms, but obviously becomes much worse with lag spikes just like any other game. These calculations become much more stable if you include momentum as well, since a player can no longer instantly change their velocity by 180 degrees. Including player momentum would constrain just how large the correction vector can be.

3 Likes

That’s good if you really care about positional accuracy, generally though for most applications, it’s kinda fine to not bother correcting the position, and just move towards the new position a bit quicker or slower.

One benefit of this solution however is that you aren’t “behind” by a network frame

1 Like

I created a little “fake” server for the simple purpose of trying out different things.
It allows for setting your latency, interpolation delay and whether you want to interpolate or simply apply the latest state received. It’s pretty bad code, but it’s enough to test other implementations

Without interpolation: https://playground.babylonjs.com/#ZDJ98Z#5
With interpolation: https://playground.babylonjs.com/#ZDJ98Z#6

EDIT: Accidentally left the fake latency hardcoded

4 Likes

@Raggar this is really useful! Thank you!! Is this interpolation method chasing the actual current position?

@MackeyK24 I think that’s a good step towards getting the functionality you’re after. It looks really smooth in the interpolated example. If that is good enough, then you can just generalize that code for your toolkit.

Yo @Chisato_Sozo i got a couple questions about this implementation.

First of all how are you using performance.now at the time here

const curDelta = performance.now() - target.timestamp;

I mean what value is being stored on the entity as timestamp. In my solution the server is being tick at 60fps accumulating delta time and STAMPING the entity updates as they go out.

How can a clients local performance.now time ever work with entity.timestamp. I gues i need to see how you are creating the entity timestamp.

I dont know react so i can tell alot of what is going on in the code snippet. So i cant tell what is going on with the buffering… so i dont know how or what is LAST and TARGET snapshot you are using for the position and rotation lerping

Yo @Raggar … thanks bro… i going to study that code and see if i can tell what is going on with my shit. I still dont see how local time can be used to check against the entity.timestamp…

For me… the entity.timestamp is set at the server using the serverTime… Which starts a ZERO when the server room instance is created in match making… then ticked with the the server delta time in the server game loop running at 60 fps. How the heck is the local time used for that in a real server situation.

I gotta take time to study what is going on in your code example. THANKS FOR THE EXAMPLE :slight_smile:

P.S. I dont know if its just me but i still see a bit of jitter in the example with interpolation. Just a little… It is not as smooth as the green cube. I dont know if that is just as good as it gets for networking or what… Should i NOT expect the movement of the network entity to EVER be as smooth as local movement, even with interpolation ???

1 Like

if the client sends updates at a specific interval, i dont think you have to trust the actual time, instead, i think you can use the delta from the previous time to basically reconcile ping variance from frame to frame.

2 Likes

btw this might help GitHub - virbela/buffered-interpolation: A class for interpolation of position, rotation, and scale for networked BabylonJS objects.

4 Likes

Are you kidding me, Where in the XXXX was that.

Its usage looks simple, is there some kind of timestamp from the server it is expecting or what ?

1 Like

there’s a post on it and page about it in the docs, just added not too long ago

here is a good 2d example that illustrates concepts
.GitHub - zenoplex/authoritative-game-server: Authoritative game server demo with node and socket.io

1 Like

Shit how would i use that in regular SCRIPT tag javascript page… I dont use the npm stuff

1 Like

I have a server going… in Colyseus… I just need to smooth out remote entity movement… I going be looking at that project as well as @Raggar demo

Thanks guys for giving me some stuff to look at…

NOTE: I am still willing to pay someone to actually help me out with the coding

idk, transpile it to javascript using the typescript compiler and replace imports with const { mod1, mod2} = BABYLON

there are several strategies i think, each with different tradeoffs, i think the term you’re looking for smoothing on the client is “client side prediction”

here is another 2d example illustrating that concept alone
.GitHub - bytezeroseven/client-side-prediction: Client side prediction and server reconciliation implementation with a little anti speed hack thingy.

one tradeoff i think is how many snapshots you take, like 1, 3, or 5 and the ms between each frame. trading smoothness for bandwidth and latency. not sure if thats completely accurate, but something along those lines. in the case of client side prediction, you first predict , but you still have to wait on the server to send the truth, then reconcile that with the prediction. if they match perfectly, there wont be any jank, but of course, there will be a little. i think that’s the little jank you’re seeing in rag’s demo. i believe widening the frame to 3 helps fix this, but requires more bandwidth and adds a frame of ms to player ping. there’s a lot of good gdc talks too

SHIT… I STILL GET JITTER

I tried the GitHub - virbela/buffered-interpolation: A class for interpolation of position, rotation, and scale for networked BabylonJS objects. library for moving the transform and its the same jitter.

What in the entire heck ???

No, it finds two states to interpolate between - some time in the past. This is done, so in case of a missed or late update, you, usually, have enough states to interpolate

There is certainly jitter present. And no, there shouldn’t be. Must be an issue somewhere in the code. We interpolate between two known states, so I don’t quite see how it can jitter in such a manner. It could potentially be the frame jitter caused by the fixed loop, but in that case, it shouldn’t exceed the jitter of the “server” mesh.

Using a timestamp is easier. Normally you would pack all your messages/states into a single serialized packet, and only send one 8 byte timestamp and use that for all entities, events etc.
In your case, you’ll need clock synchronization to get the client clock as close to the server clock as possible. If you only need to network a kinematic physics body, you don’t really need to sync, but if you want to network an actual deterministic physics simulation or events, you’ll need to sync client and server. I haven’t done clock sync before, but I am doing tick sync instead.
I have based my approach on what Overwatch does. I first synchronize the initial tick, then I speed up or slow down the client simulation based on whether I’m ahead or behind the server. This is done in order to fill or empty the input buffer on the server. You want the server to receive inputs right about when it’s needed+some jitter buffer based on client network stability.
An example is this, for three frames on the client, you apply three different inputs [left, forward, right]
Frame1, input1: body.setVelocity(-5,0,0)
simulate()
Frame2, input2: body.setVelocity(0,0,5)
simulate()
Frame3, input3: body.setVelocity(5,0,0)
simulate()
This puts you at (0,0,5)
But if the server receives and applies all three at once, it will look like the following:
Frame1, input1: body.setVelocity(-5,0,0)
Frame2, input2: body.setVelocity(0,0,5)
Frame3, input3: body.setVelocity(5,0,0)
simulate()
And puts you at (5,0,0)
Resulting in having to correct this error at the client.

Take a look at the following thread: https://www.gamedev.net/forums/topic/696756-command-frames-and-tick-synchronization/
And some code: GitHub - minism/fps-netcode: Quake-style multiplayer FPS demo that integrates a number of modern netcode techniques for quality of gameplay.

This is the “modern” approach to networking, made popular by Overwatch and Rocket League.
I have based my netcode on this, and on a stable connection I have a completely deterministic networked physics model. Even on less stable networks, within a short time span, the simulation is back at sync. Bad networks usually get a slightly(growing) bigger jitter buffer

This might not be what you’re after, at all, but it’s simple to implement, and works very well.

1 Like

I keep pitching for us to use a deterministic model but it keeps getting pushed back on by the team.

Ill hit you up today Mackey and see if there is anything obvious that might be missing or off.

Are you kidding me, Where in the XXXX was that.

Lawl.

No matter how hard you google, there will always be a search term that you missed, at least that’s how it’s been for me.

1 Like

Okay, it wasn’t a coding error on my part(Bet there are, but this wasn’t the issue). The fixed loop adds a little bit of jitter, which can be countered with the frame interpolation from the other thread. The real issue here seemed to come down to tuning.
If the server sends out updates every 100 ms, we need to grab the two states At Least 200 ms in the past. 100 ms per state + our latency. Another issue is the circular motion of the animation. If you start plotting points around a circle, you’ll get get that round saw pattern that the jitter displays. You wont normally see a player run around in perfect circles, but the way to counter it, is finding a blend between update rate and how far back in time you go.
You often see action games run at higher Hz than 10.

Here’s a visualization of the issue, by sending states at 5Hz(200 ms) with a latency of 100 ms and interpolation delay of 400:

Compared to sending states at 20Hz(50 ms) with the same latency and interpolation delay of 100 ms

Much smoother. The more time between server authoritative updates, the farther back in time you need to go in order to get two valid states(no surprise). The more updates the server sends, the more precise of an interpolation you’ll get, which again, should come as no surprise.
20Hz seems enough for even a circular path, which linear interpolation doesn’t handle too well. And this is even low for most action games like FPS- and racing games.

2 Likes

Edit: OK… I am officially back in it.

So i am using this library GitHub - virbela/buffered-interpolation: A class for interpolation of position, rotation, and scale for networked BabylonJS objects. as the default interpolation method.

So you have three interpolation modes to choose from using my Network Entity Script Component:

  • None (Snaps to positions)
  • Default (Buffer interpolation library)
  • Custom (You can register an interpolation handler and write your own custom interpolation as well as any other client side prediction algorithms you like for your project)

I am STILL messing around with the additional client side smoothing, like maybe smooth follow ghost network target positions, or something like that… maybe a MOVE TOWARDS the new interpolated target position… Something :slight_smile:

2 Likes

to use UDP instead TCP, UDP is more faster, greetings
if you need help about it, write me, (i not use NPM to front, only to back)