Search Unity

Bug Jitter with Manual Physics Update

Discussion in 'Cinemachine' started by JohnSheppard92, Apr 25, 2023.

  1. JohnSheppard92

    JohnSheppard92

    Joined:
    Jan 27, 2018
    Posts:
    12
    Hi there,
    I'm currently working on a multiplayer game in which you can fly a vehicle around in 3D space basicly freely. I have build it with a 1st person cockpit camera system but recently started implementing a 3rd person camera system aswell using cinemachine.

    The way we do movement in our game is a little different, because we use a self developed client-prediction system to work with the new netcode for gameobjects package and we have to manually trigger the physics update, when we need it. Cinemachine seems to really not like that, because no matter what I do, I get lots of jitter in the 3rd person camera which seems to actually not come from the object the camera is following as far as I can tell. The 1st person camera is much smoother because it doesn't have any LookAt or Follow properties set, it's basicly just a dumb virtual camera parented directly to the player, with that said it still does indeed suffer from some jitter. We started development for VR and had a XR Rig as our camera system, but recently droped VR from this project. The XR Rig never experienced any jitter at all.

    I've tried to strip the project down to the absolute essentials even removing all the multiplayer stuff in a test project and the problem seems to persist, so I've uploaded the test project to fiddle around with here:
    https://www.dropbox.com/s/7jl9f4wtbpg6esx/CinemachineJitter.zip?dl=0
    Created using Unity 2022.2.6f1

    The classes that are relevant for the movement itself are the PlayerSimulator and the PredictionInputManager attached to the Player Object, though they are stripped down heavily. I've also implemented some debug-lines to visualize the movement of every relevant object. In the test project you will find a single scene with a player and 2 virtual cameras, one attached to the player itself and one free-floating, setup to follow the player around. Just change the priority setting of one of them to switch between them. The 3rd person camera jitters extremly much, the 1st person camera shows the player completly smooth but does suffer from some jitter that can be seen best while rotating and looking at objects that are further away. I've placed some cubes to make that more visable. The player and both the virtual cameras will draw a debug-line each late-update (or the player itself each manually triggered fixed update) that show absolut regular movement for the player but both cameras show very irregular movement, here is a video showing everything:


    debug lines at the end:
    green & red - Player movement
    magenta - 3rd person camera
    blue - 1st person camera

    I've seen a couple of posts with jitter problems here, I tried using interpolation on the rigidbody and setting the cinemachine brain to update on LateUpdate without much success. I've even tried manually updating the cinemachine brain on each triggered physics update, that also didn't help, so I'm at the moment out of ideas and could use some help.

    Thanks in advance
     
  2. antoinecharton

    antoinecharton

    Unity Technologies

    Joined:
    Jul 22, 2020
    Posts:
    189
    Heyyoo :)

    Looks like the issue comes from here.

    Code (CSharp):
    1.        
    2. private void ProcessDebugPlayer()
    3.         {
    4.             float dt = Time.fixedDeltaTime;
    5.  
    6.             clientTimer += Time.deltaTime;
    7.             while (clientTimer >= dt)
    8.             {
    9.                 clientTimer -= dt;
    10.  
    11.                 PlayerInputs inputs = ReadInputs();
    12.                 playerSimulator.ApplyInputsToPlayer(inputs, dt);
    13.                 Physics.Simulate(dt);
    14.  
    15.                 Debug.DrawLine(playerSimulator.LastPosition, playerSimulator.RbPosition, Color.green, 10f);
    16.                 Debug.DrawLine(playerSimulator.RbPosition, playerSimulator.RbPosition + Vector3.up, Color.red, 10f);
    17.                 // Have tried Manual Update here
    18.                 //brain.ManualUpdate();
    19.             }
    20.             // Have tried Manual Update here
    21.             //brain.ManualUpdate();
    22.         }
    23.  
    I have honestly no idea if this is the correct way to override physics. Have you checked or asked on the physics forum if this is the correct implementation? Your delta time needs to be perfectly accurate.
     
  3. JohnSheppard92

    JohnSheppard92

    Joined:
    Jan 27, 2018
    Posts:
    12
    Hi, thank you for the reply! We got this from a guide for a client-side prediction system and until now it served us pretty well. Here is the guide:
    https://www.codersblock.org/blog/client-side-prediction-in-unity-2018
    We changed much of it to work with Netcode for GameObjects and serve our specific needs, but at it's core it is quite similar. I'm not sure if it is the best way to do it honestly, I'll check if I can find anything about this. But the thing that confuses me is, that even in VR with the XR Rig parented directly to the Player, we never had any issues of this sort, it just was perfectly smooth. I really hope there is something we can do to fix the issue without changing the core of how we move things in our game too much.
     
  4. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    The paradigm when using physics interpolation is this:
    • The physics simulation runs on the FixedUpdate clock. RB positions are valid only at the specific frame times in FixedUpdate.
    • The render clock (Update and LateUpdate) has a variable framerate and is not aligned with the FixedUpdate clock. The GameObject transform positions are used for rendering.
    • Without interpolation, the RB positions from the last fixed frame will be copied every frame into the transforms. In general, the result will not be smooth because of the mismatch between the render clock and the physics clock.
    • To combat this, interpolation simulates one fixed frame ahead, and patches the transforms (not the RB positions) with an interpolated value computed from the last 2 physics frames, making the motion smooth from the render perspective. This means that scripts - including Cinemachine - can rely on the transform positions being correct and matching the current Time.deltaTime. This is very powerful and effective.
    If you correctly set up interpolation on your Rigidbodies, then Cinemachine (and other scripts) can rely on the transform positions and everything will be smooth. To make interpolation work, you have to follow some rules and cannot do just whatever you want with the rigidbodies. These rules include:
    • Use only sanctioned API (e.g. AddForce, velocity, angularVelocity) to modify the RigidBodies - never set the positions/rotations directly. It's easy to break interpolation by changing the RB using the wrong API. What is the "right" API? That depends on whether your RB is kinematic.
    • Modify the RB only in FixedUpdate, never in Update

    The problem in your scene is that your method of driving the physics loop is preventing interpolation from working, and so you are getting jitter because you have set up Cinemachine to assume that interpolation is on. If I change your PredictionInputManager.cs as follows, then everything is butter-smooth.
    Code (CSharp):
    1.         void FixedUpdate()
    2.         {
    3.             PlayerInputs inputs = ReadInputs();
    4.             playerSimulator.ApplyInputsToPlayer(inputs, Time.fixedDeltaTime);
    5.             Physics.Simulate(Time.fixedDeltaTime);
    6.         }
    7.  
    8.         private void LateUpdate()
    9.         {
    10.             Debug.DrawLine(transform.position, transform.position + Vector3.up, Color.red, 10f);
    11.         }
     
    Last edited: Apr 28, 2023
  5. JohnSheppard92

    JohnSheppard92

    Joined:
    Jan 27, 2018
    Posts:
    12
    Hi there @Gregoryl, and thank you very much for your reply. I've investigated your suggestions and we updated a few things on our side to now run the physics simulation back in the FixedUpdate. This will smooth out any issues with camera jitter for the host, so thats extremly awesome!

    Sadly because of our prediction system we still have a lot of jitter on the client side of things. Basicly we send every input of every player to the server each tick, the server and the clients then run simulations and the server sends the positions and rotations of each player back to them. When a player detects a deviation between it's own simulations and the ones received from the server, it will rewind back to the tick where the deviation occured (so it will teleport the player back), correct that output and rerun the simulation to the current point in time all in that one FixedUpdate (which works quite well actually). This also means, that the teleport is never actually rendered, since the simulation reruns all in the same FixedUpdate. Usual deviation is so small, that this is inperceptable for the Players. This will only really be percivable when the deviation is very big, which should only occur when players cheat or the ping between them is very high (we know there are still some issues with this approach because of the rewinds. We will probably limit the amount of steps that can be rerun and will just teleport the player if the number is exceeded)

    Problem is, this seems to make Cinemachine jitter a lot again, though I'm not 100% what exactly causes it. I will try to combat this with a custom Camera Rig that will follow the players around but one physics step behind, so we can manually interpolate, but I'm unsure if this will actually fix the problem.

    If you or anybody else has any suggestions how to combat this more effectivly, I'm still very open to them :)
     
  6. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    If you enable certain features in Cinemachine (such as damping and lookahead), CM needs to maintain its own motion history in order to deliver those features. If, for whatever reason, you're rewriting the motion history of your targets and teleporting them, you need to make sure that CM's history is also rewritten, or you can get jitter because CM's idea of the target position won't match the actual one.

    You can test that this is the cause by turning off all CM damping and lookahead. If the jitter disappears, then this is the culprit. In that case, there is something you can try: when your target gets teleported because of a history change, call CinemachineCore.OnTargetObjectWarped() with the target and its position change as parameters. This will iterate all the vcams that are targeting the teleported object, and rewrite their histories to take the position warp into account.
     
    gaborkb and JohnSheppard92 like this.
  7. JohnSheppard92

    JohnSheppard92

    Joined:
    Jan 27, 2018
    Posts:
    12
    Thank you so much, this worked like a charm. We still have some minor jitter on host and client, but I'm sure I'll be able to somehow fix that myself. But overall this has seemingly fixed our issues, so I'm finally able to fully use Cinemachine! \o/
     
    antoinecharton, gaborkb and Gregoryl like this.