Simple replicated physics jittering

So there’s something that I must be missing, but it sounds like the very simplest case I could think of replicated physics doesn’t behave as I would expect it to.

The setup is as follows:

  1. BP Actor + Static Mesh Component
  2. Actor Replicates and Replicates Movement = true
  3. Tried setting the component to replicates on/off, no difference.
  4. Component is set to Simulate Physics, and Linear Drag is set to 0.

I also have a flying player character to follow along. On BeginPlay I apply a Physics Linear Velocity to the Actor, setting it’s velocity to a fixed value, let’s say 15000,0,0.

Now I would expect this to be replicated and work flawlessly, server sends the client the velocity, and periodic location updates/corrections. But that’s not the case, the Velocity jitters.

As you can see from the following logs, the velocity at the server is constant, yet the client’s velocity varies randomly above or below the set value.

LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Server: TEST = X=15000.000 Y=0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Client 1: TEST = X=14901.105 Y=-0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Server: TEST = X=15000.000 Y=0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Client 1: TEST = X=15005.309 Y=0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Server: TEST = X=15000.000 Y=0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Client 1: TEST = X=14901.103 Y=-0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Server: TEST = X=15000.000 Y=0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Client 1: TEST = X=15005.307 Y=0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Server: TEST = X=15000.000 Y=0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Client 1: TEST = X=14901.102 Y=-0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Server: TEST = X=15000.000 Y=0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Client 1: TEST = X=14797.620 Y=-0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Server: TEST = X=15000.000 Y=0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Client 1: TEST = X=15005.305 Y=0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Server: TEST = X=15000.000 Y=0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Client 1: TEST = X=14901.100 Y=-0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Server: TEST = X=15000.000 Y=0.000 Z=0.000
LogBlueprintUserMessages: [BP_TEST_C_UAID_50EBF62291BF856E01_1521310727] Client 1: TEST = X=14797.619 Y=-0.000 Z=0.000

Now the only more or less reasonable explanation I can find is that the Velocity itself is not being replicated, but calculated on the client based on the position delta reported, and this somehow introduces some error?

Of course the higher the speed, the more noticeable this error is.

I would had expected for the server to replicate the bodies velocity, and both simulation to run in sync.

The result of this discrepancy is that there’s some jittering visible on the moving object relative to the player’s character (it “rubber-bands”).

I would appreciate if anyone can shed some information on this mystery.

Cheers!

1 Like

Server is replicating the location/rotation per server tick.
e.g. replicates movement

Client is lerping to the location/rotation once the packet is parsed. bare bones replication of movement.

You aren’t explicitly telling the server to replicate the velocity. even if you did there would still be jitter (server correction).

Rubberbanding is the correction to servers position at game time.


The server should be the only driver of movement. The client shouldn’t have any input at all.

Otherwise you need to implement client-side prediction w/ server authority and correction.

1 Like

I understand the reason why it’s happening as detailed in the post, the question is, is there’s no way for Unreal to properly handing networking of a Physics object moving at a constant speed? Sounds like a very basic case for it to be broken. Why doesn’t it replicate velocity properly? (it seems the client is “calculating” the velocity based on the position delta.

It’s a matter of who owns the object moving having authority over its transform.

If the server owns it, it should be the only one moving it. Otherwise there’s conflicts in transform.

If the server is moving this actor, then the client doesn’t need the velocity to make any local corrections. Any/all corrections it does make will be overridden by the servers next update.

Replication corrections are always teleport, unless coded to interp smoothly over n frames.


Can you post a vid of the jitter.

1 Like

Whoever has control over the moving thing also has control over the transformation it undergoes.

In that case, it is only appropriate for the server to relocate it. Otherwise, transform incompatibilities will arise.

If this actor is being moved by the server, the client does not need to know its velocity in order to make any in-game adjustments. The next update to the server will overwrite any changes it makes.

Unless specifically configured to interp seamlessly across n frames, replication corrections always take the form of a teleport. basketball stars

So what you are saying is that the physics simulation is done on the server, but the client gets only notified of transform updates, and teleports the object between updates, and the “movement” I’m seeing is mostly from temporal buffers?

Even if the server is moving the actor, the client should need the velocity to properly interpolate between server updates. If both client and server are running the same simulation, without any changes to the linear velocity, the server updates should match what the client predicted.

Which brings me back to my question, how come Unreal doesn’t handle properly replicated physics objects by default?

Here’s a video of a quick clean template using the process detailed in the first post.

So if I understand correctly, in order to get a simple box moving, I need to create a custom Actor, receive position updates from the server and do the interpolation myself instead of setting that server transform? If you don’t know how quickly to interpolate you would end with a rubberbanding effect anyway, when the object slows down reaching the destination. The only way to make the movement smooth that I can think of is to replicate the velocity so both sims match (which is what I expected unreal to be doing with “replicate movement”).

Also, enabling/disabling “Component Replicates” in the Static Mesh inside the Actor that has the “Simulate Physics” enabled, makes a huge difference on the jittering.

If the actor pre-exists in the level then physics replication will always be a catchup jittery mess.

You need to spawn the actor on the server. To test, spawn it via the level blueprint.

The cons to physics actor replication is it is not deterministic at this point. Meaning you can and will get different behaviors at times. Depends on server tick rate, replication update rate, client ping. Also they’ll never truly be in sync. The server is always n milliseconds ahead of the client in regards to it’s owned actors.

Another option outside of physics movement is the projectile movement component.

Spawning it only on the server (from the Level BP) doesn’t seem to make any difference in my setup. Same results.

The only thing that “improves” things is disabling “Component Replicates” in the static mesh, only leaving Replicate Movement in the actor as true.

I guess they are fighting each other on what to replicate?

If it smooths out with component replicates unticked, then just leave it at that. Also make sure your Actor tick rate isn’t 0.0… It needs to be grounded to a max of 60Hz, better at 30 or 15Hz… maybe even 10.

I haven’t noticed any differences in this basic setup with tweaking the tick rate, but on a real scenario, would you recommend every object to tick at a fixed rate?

It has always puzzled me the fact that UE ties the tick to the framerate vs having a fixed timestep.

By default it’s always 0.0 for development purposes. New users would be even more stumped by jitter etc from a default fixed step.

Being performance technical if the actor doesn’t require ticking, then it should be disabled outright. Most actors don’t need ticking. I disable at creation, unless I need tick.

Overall each actor that does tick needs to be tuned to the lowest possible frequency you can get away with, without sacrificing on the end goal. What the actor does also has an impact on the tick rate. But in general they should never tick faster than the server if it’s multiplayer based.

e.g. a server controlled turret should at max tick with the server. Server is set to 30Hz, then that actor should be set to tick at 30Hz (preferably 15Hz in this example).

With server ticking at 30hz you mean a dedicated server right? A listen server still ticks at the freq of the user’s frame rate?

Yes Dedicated, but you can cap listen in the ini’s.

example…

BaseEngine.ini

[/Script/OnlineSubsystemUtils.IpNetDriver]
bClampListenServerTickRates=true

Ok, I’m still playing around with this, debugging and trying to understand what goes on.

I now have a bit of a different setup (the simple example was too simple). I’m setting the velocity on the server, setting it as a replicated variable, and then re-setting the velocity every tick both on the client and the server to this value. For this test it’s a constant velocity, I’m not even changing it.

Now I print on the screen the result of “Get Physics Linear Velocity” on the mesh, the server shows the correct constant value, but the client one jitters around the value I set, but never exactly that value.

I added some on-screen debug lines and text to debug the actors position, velocity and if it’s the authority or the simulated proxy.

What I’m seeing is that the Simulated one slowly drifts ahead of the server, until eventually it snaps back (im guessing this is the hard snap teleporting the object back in place), and then it starts all over again.

So it seems the velocity is calculated on the client side (maybe by the last delta position?) and then used to predict the next few frames, but for some reason that velocity is always off by a few decimals.

I made a video demonstrating what I’m talking about to make it clearer:

On the top left you can see the screen messages showing the velocity on the server and the client as reported by Get Physics Linear Velocity.

By the way, tweaking the “Max Linear Hard Snap Distance” setting under Project Settings → Physics affects the hard snap seen on the video, that’s why I’m assuming that’s the point where it makes a hard snap. If I set the hard snap distance to 0, then the positions never drift apart, but of course it’s very jittery.

Ok, more research, more updates.

The Velocity IS replicated by bReplicateMovement. This is done in AActor::GatherCurrentMovement, which after a while gets updated in the async physics tick and set back on the client side object.

I found out that the reason for the velocity being ahead on the client is down to this coefficient:

void FPhysicsReplication::ApplyAsyncDesiredState(const float DeltaSeconds, const FAsyncPhysicsRepCallbackData* AsyncData) 
{
...
   const FVector NewLinVel = FVector(State.LinearVelocity) + (LinDiff * LinearVelocityCoefficient * DeltaSeconds);
...
}

I tweaked the coefficient in the project settings and set it to 0, and the jittering velocity on the client side disappears. I think this extra velocity is added to try and have the client side object catch up to the server one.

Sad part of the story, is that even setting this to 0 and having the velocity stable on the client side, there’s still jittering, so back to debug mode :slight_smile:

First issue is you are setting the “Velocity” on an actor the client DOES NOT OWN. The actor is owned by the server, thus Authority over its Velocity and Transform is governed by the server.

Every time you change the velocity you are changing its position in your simulation. The server Updates its sim and sends you the new velocity and position. Your client updates to that, then ticks and makes a new change. This == JITTER!

They will NEVER sync. Simulation on the client proxy is ALWAYS behind the servers current and WILL ALWAYS BE.

Once you start dealing with PINGS, Ping Variance and Packet loss you’ll see more jitter if you continue trying to sync to the server 100%.

Replication of position is and will always be an Approximation. Never will be a perfect 1:1 per frame.

Simulated proxies (Your View) Interpolates to the new position over n frames. Your framerate varies. The servers does not.

The server should be static at say 30Hz. Whereas you and any other client can vary from 10 FPS to 244FPS. Then there’s 1% and 0.1% lows.

The client will adjust the amount of movement needed over distance and FPS to get to the “approximate” position at xxxxxxx game time.

The only way around this is for you to NOT replicate the servers actor and to spawn your own. Then you’ll be ahead of the server, but it will go exactly the velocity you want at all times. Yet everyone elses sim will be behind yours. Cause PING, Variance, Loss, processing delays, Framerate variation etc.

1 Like

I understand that, the reason I tried it this way is that I’m setting a CONSTANT velocity, so it’s never changing! And I wasn’t sure why it was changing on the client.

Anyway, with the later investigation on why the velocity fluctuates on the client (the coefficient) I removed the force-setting the velocity on the client, simply setting the coefficient to 0 shows that the velocity has a correct value on the client simulation as well.

Yes, I was expecting this as well, but if you watch the video, it’s the client that drifts AHEAD of the server, which is why I’m investigating all of this in the first place. (In the video the “floor” is moving to the left of the frame).

The coefficient applied to the velocity calculation is always adding velocity, resulting in the client overshooting the server’s position every single frame.

I’m thinking the solution was simply put in place and tested with “fps player movement speeds”, and nothing higher. I’ve been tweaking the physics sim values for a while now, and even though I got better results (no visible jitter) I can still see both simulations drifting apart, even with a constant velocity.

I’m about to give up and just try to implement my own movement replication in my actor/pawn and see what walls I hit. Might help me understand what’s going on better.

PS: trying to come up with a sample project that shows the problem.