Predicted Entity Spawns Cause Player Movement Jitter?

Hi again!

I’m running into jitter for my predicted player movement whenever I spawn a predicted entity into my world. Some details on my setup:

  • IInputComponentData used for input gathering in InputSystem within the GhostInputSystemGroup
  • My PlayerMoveSystem sets the PhysicsVelocity component of my player and this system is within group BeforePhysicsSystemGroup
  • I’m instantiating predicted prefabs (game abilities) in a system within the PredictedSimulationSystemGroup (note even basic predicted prefabs with no relation to my player entity will cause jitter too)
  • Predicted prefabs are instantiated with the BeginSimECB
  • Jitter/Movement Glitch is far more noticeable at lower network/server tick rate of 10hz
  • I’ve checked and rechecked that I’ve included WithAll tag in all my predicted systems

I’m really at a loss for what could be causing the jitter and any thoughts on this would be greatly appreciated!

Does this happen in builds as well? The editor tends to have some jittering that does not occur in builds (no idea why that is).

It could be caused by server tick batching (which is enabled by default in 1.0), due to low editor performance. Disable it by setting ClientServerTickRate.MaxSimulationStepBatchSize to 1.

It causes indeterminism by batching multiple server ticks together, likely clobbering inputs.

Given your understandable uncertainty with this use-case, and the fact that it’s technically a failure case (server perf too low), we plan to add better logging of it in the near future.

Edit: Note another gotcha with this batching mechanism: It doesn’t batch physics steps (yet).

slickgotchi, if you can rule out batching, please send us a repro project with a bug report.
Question: Does your PhysicsVelocity component have a GhostField enabled variant on it? Ditto for other physics components (like PhysicsDrag)? If not, rollback and resimulation will not work successfully.

@NikiWalker so just to be sure, is it currently a recommendation to disable tick batching, or is this purely something that is advised while in editor context?

We enable it by default because it’s extremely valuable for preventing death spirals in prod DGS environments, in the (hopefully rare) cases where your server is unable to keep up with real-time.

However, in the editor, it is very common to experience poor performance for perfectly valid reasons (e.g. burst off for iteration times), and therefore:

  • We should do a better job of notifying users that it’s enabled and affecting prediction quality.
  • And that you should disable it when you want to debug client prediction quality i.e. character controller prediction soft determinism.
  • Otherwise, we’d recommend keeping it on for a better editor framerate / experience.
1 Like

I still hope official can make editor faster to solve this problem to make both editor and player runtime build using the same config setup and make both of them behave exactly the same. I think why it’s really slow at editor is caused by both client and server all at one single editor process. Any plan to move both client and server out and become 3 independent processes i.e. editor process, client process and server processes but unity profiling and etc can work properly at client and server at editor without required build as player runtime build?

This is common in production client hosted setups, too. The editor is slow for a variety of valid reasons. E.g. (non-exhaustive):

  • Burst being disabled for iteration times.
  • C# compiled in Debug for debugging & iteration speed (and no IL2CPP).
  • Background tasks like compiling, importing, baking.
  • Attached debuggers, especially when breakpoints are added.
  • Additional safety overhead (jobs, leaks, collections etc).
  • Editor overheads (UI like Hierarchy, Inspector, and Scene view, profiling etc).

Yes. In the short term, we’re adding support for Netcode for Entities PlayMode Window to MPPM (https://docs-multiplayer.unity3d.com/mppm/current/about/), and we’ve got ideas longer-term to improve this further.

3 Likes

Side note: MPPM will not solve the problem in the editor if you are using debug mode. It will be still pretty slow (actually make things slightly worse and may cause other issues, for example tick rollback because both client and server either runs to slow or fail to sync up the time).
We did a lot of improvement in 1.2 in that sense (we removed a lot of overhead when burst is disable) so the problem is a little mitigated now, in comparison to what it was for 1.1.

The default settings we should have in the editor is to not batch. Having it as an opt-in, not an opt-out is better for both clarity and understanding of the current server/client functionality.
But from a user-experience point of view (and perf) was an obvious win at 1.0 time, and it is in general a good strategy for developing and avoids spiral of death in the editor (especially with physics on).

1 Like

Unfortunately, adjusting MaxSimStepBatchSize in my global NetcodeConfig did not seem to change it.

I haven’t enabled GhostField’s on these physics components. How do I do that for the in-built components?

I’m new to bug reports, do I just make a thread here and then send through a link to my github repro project?

There are many things that can cause misprediction.
In general, RULE OF THUMB: any state you modify or you use inside the prediction loop should be a replicated field, unless it is a readonly constant or you absolutely know it is a dependent variable it is always refreshed because of other replicated field changes.
And this because of the rollback and resimulate logic.

When it comes to physics, we already replicate PhysicsVelocity (that it is the only thing necessary for physics to work) so for these components you don’t need to care. But if you have other component used by physics simulation, I would strongly suggest to replicate them.

If you just use physics velocity to move objects then no worries.

One thing you can do, while not clarify the whole picture, is to actually start logging the value of the transform for both client and server for a given tick. On the client in particular because of partial ticks, you will resimulate the tick multiple time (from the last backup) so you would end up with a log that looks like this:

[Client] 998 …
[Client] 999 …
[Client] 1000.ServerTickFraction : Transform X: Physics Y Input: Tick, x,y,z …
[Client] 1000.ServerTickFraction : Transform X: Physics Y Input: Tick, x,y,z …
[Client] 1000.1: Transform X: Physics Y ← FULL TICK Input: Tick, x,y,z …
[Server] 998.1: Transform Y: Physics Y Input: Tick, x,y,z …

I would suggest to put logs before and after the prediction loop so you get the start and end value after all the computation.
Now you can compare for tick 998 what the value calculated by the server was and what input it used. That would help immediately understanding what discrepancy there are in the computation.

That being said there is another source of Jitter that is caused by physics interpolation (in case it is used) if your camera follow that entity. The movement you will see it is substantially a jittery back and forth of the transform.
In that case, and almost in all cases, it is more correct to always use the LocaltToWorld matrix and get both the Translation and Rotation to follow from there. That would remove any jittery motion seen from the camera point of view.

Another potential jitter (for physics) is introduced by quantization. To understand if that is the case, either write two custom variant for transform and physics velocity that remove the quantization (Quantization=0 in the the GhostField) or change the default implementation in the package to remove quantization.
If that remove the jitter you are experiencing, the issue it is due to how we predict physics client side in comparison to what server does.
In particular, because client simulate multiple ticks when re-predict (and the same goes for partial ticks as well), it is 'incorrectly" using the last computed state for simulate tick N+1. However, when it receive the state from the server for tick N, it will receive that quantized. So the starting point of the simulation, while not extremely different, it is still not exactly the same, and may result in a different computation. This is especially visible in presence of ramps in certain cases.

This just to mention a couple of sources. Sure, please file/open a bug report for this, so we can use your project and understand what it is going on.
You can open the bug report directly from the unity editor (help/Rerpot Bug) and add a zip with the repro project.

1 Like