Search Unity

Question Cinemachine teleport with dampening stutter

Discussion in 'Cinemachine' started by Lucideus, Aug 16, 2022.

  1. Lucideus

    Lucideus

    Joined:
    Dec 7, 2015
    Posts:
    25
    I used the search feature and found threads talking about this and they were marked resolved. However, to my experience, it's not - or at least it's back.

    I'm resetting the floating origin and each time it resets there's a stutter happening to the player which the camera is tracking and which acts as the focus point for the origin. Without any dampening applied to the virtual camera there's no issue at all, but with it applied there's a stutter each time.

    Code (CSharp):
    1.         private static void MoveCinemachines(Transform target, Vector3 offset)
    2.         {
    3.             int vCamCount = CinemachineCore.Instance.VirtualCameraCount;
    4.  
    5.             for (int i = 0; i < vCamCount; i++)
    6.             {
    7.                 CinemachineCore.Instance.GetVirtualCamera(i).OnTargetObjectWarped(target, offset);
    8.             }
    9.         }
    For all I know, this is all there should be to it...
    I've also tried setting the Standby Update to Always as well as None but no luck. I also tried fiddling with the Cinemachine Brain settings but no difference there either.

    I've seen suggestions trying to set the PreviousStateIsValid to False, which made it even worse.

    https://imgur.com/2IwtCcO Here is a small clip displaying the issue I'm having. Note, I don't have any dampening applied in the Z axis here. If I do apply dampening to the Z axis the same happens in that direction too.

    I'm on the latest 2021 LTS version with the latest Cinemachine version as well.
    Any ideas as to why this is happening, and/or suggestions how I can go about fixing it?
     
    Last edited: Aug 18, 2022
  2. Kreshi

    Kreshi

    Joined:
    Jan 12, 2015
    Posts:
    445
    Have you applied the offset to the virtual cameras transform position before calling OnTargetObjectWarped? I haven't tried it but there is a chance it could help :D.
     
  3. Lucideus

    Lucideus

    Joined:
    Dec 7, 2015
    Posts:
    25
    Mind elaborating how (in case I got it wrong)?
    I just tried it but that just caused some really weird behaviour, just making super large jumps, haha. Thanks for the suggestion though.
     
  4. Kreshi

    Kreshi

    Joined:
    Jan 12, 2015
    Posts:
    445
    My idea was: If you want to move cinemachine virtual cameras by an offset I guess you can achieve that by moving the virtual camera directly + the corresponding target :).
    If you had weird large jumps this is probably because you offsetted either the target twice or the virtual camera twice or both twice. Or you moved it by the opposite offset :D.
     
  5. Lucideus

    Lucideus

    Joined:
    Dec 7, 2015
    Posts:
    25
    Isn't that what OnTargetObjectWarped is meant to handle in this case?
     
  6. Kreshi

    Kreshi

    Joined:
    Jan 12, 2015
    Posts:
    445
    Theoretically - yes - pratically - I would like to know.
    Sometimes Unity functions don't behave 100% like to be expected.
    Take animations for example. If you use 1 Layer in Mecanim, GetIKHintPosition will provide solid values. If you use 2 Layers and a blend-mask, GetIKHintPosition is basically unuseable because it doesn't take the blending into account (or it does but does it wrong - who knows).
    So yeah - you could try to move the virtual camera + target by the offset manually instead of using the build-in function alltogether.
    Alternatively you could also try to write your code in a coroutine and use WaitForEndOfFrame before calling your function - maybe this solves the damping issue too.
     
  7. Lucideus

    Lucideus

    Joined:
    Dec 7, 2015
    Posts:
    25
    Actually, an update on that... I noticed that if I make my Rigidbody *not* kinematic it's fixed. I did make it kinematic intentionally though because from my understanding, if an object is meant to be able to affect other objects physics but not be affected by physics itself, it should be kinematic, right? Hence why I've made the player kinematic.

    So...something with it being kinematic makes it stutter like that.
     
  8. Kreshi

    Kreshi

    Joined:
    Jan 12, 2015
    Posts:
    445
    if that's the case than you should check your airplane controller code. Maybe it makes use of some previousPosition value or something similar were the offset wasn't applied to :).
     
  9. Lucideus

    Lucideus

    Joined:
    Dec 7, 2015
    Posts:
    25
    Code (CSharp):
    1.     public Rigidbody PlayerRigidbody;
    2.     [Space]
    3.     public float PlanarSpeed = 30f;
    4.     public float ForwardSpeed = 50f;
    5.     [Space]
    6.     public float Acceleration = 0.3f;
    7.     public float Deceleration = 0.2f;
    8.  
    9.     private Vector2 _inputVector;
    10.     private Vector2 _currentInputVector;
    11.     private Vector2 _smoothInputVelocity;
    12.  
    13.     private Vector2 _planarMovement;
    14.  
    15.     private float _forwardMovement;
    16.     private float _smoothForwardVelocity;
    17.  
    18.     public void OnMove(InputAction.CallbackContext context)
    19.     {
    20.         _inputVector = context.ReadValue<Vector2>();
    21.     }
    22.  
    23.     private void Update()
    24.     {
    25.         // Calculate acceleration stuff;
    26.         ProcessPlanarAcceleration();
    27.         ProcessZAcceleration();
    28.     }
    29.  
    30.     private void FixedUpdate()
    31.     {
    32.         // Move the player;
    33.         HandleMovement();
    34.     }
    35.  
    36.     private void ProcessPlanarAcceleration()
    37.     {
    38.         // Rate at which to gain/lose speed;
    39.         float velocityChangeRate = _inputVector != Vector2.zero ?
    40.                 Acceleration :
    41.                 Deceleration;
    42.  
    43.         _currentInputVector = Vector2.SmoothDamp(
    44.             current: _currentInputVector,
    45.             target: new Vector2(
    46.                 x: _inputVector.x * PlanarSpeed,
    47.                 y: _inputVector.y * PlanarSpeed * 0.9f),
    48.             currentVelocity: ref _smoothInputVelocity,
    49.             smoothTime: velocityChangeRate);
    50.  
    51.         _planarMovement.Set(
    52.             newX: _currentInputVector.x,
    53.             newY: _currentInputVector.y);
    54.     }
    55.  
    56.     private void ProcessZAcceleration()
    57.     {
    58.         _forwardMovement = Mathf.SmoothDamp(
    59.             current: _forwardMovement,
    60.             target: ForwardSpeed,
    61.             currentVelocity: ref _smoothForwardVelocity,
    62.             smoothTime: 0.5f);
    63.     }
    64.  
    65.     private void HandleMovement()
    66.     {
    67.         PlayerRigidbody.MovePosition(Time.fixedDeltaTime *
    68.             new Vector3(
    69.                 x: _planarMovement.x,
    70.                 y: _planarMovement.y,
    71.                 z: _forwardMovement)
    72.             + this.transform.position);
    73.     }
    This would be the full movement code. I assume it'd be something to do with the PlayerRigidbody.MovePosition? I haven't the faintest clue though.

    Edit: If I change the MovePosition to simply the transform.Position equivalent it works fine. Is...that a good idea though?

    transform.Translate works really well anyway but I've seen posts about kinematic rigidbodies should be moved using .MovePosition. I don't really know what real difference it makes though.

    Edit 2: So, I noticed in the documentation it says to move kinematic 2d objects with MovePosition while for kinematic 3D objects it says to use transform.position. Uh, a bit confusing but sure, I guess? Kind of problematic when trying to affect *other* physics objects though.

    Edit 3: Well now I feel a wee bit stupid. Resorting to simply using rigidbody.position worked just fine. Derp.

    Edit 4: Lots of edits. I spoke too soon, rigidbody.position is unreliable to use for this it would seem.
     
    Last edited: Aug 17, 2022
  10. Kreshi

    Kreshi

    Joined:
    Jan 12, 2015
    Posts:
    445
  11. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    That won't work. CM cameras compute their own positions based on the follow rules you set. Anything you put here will be ignored and overwritten.
     
    Kreshi likes this.
  12. Lucideus

    Lucideus

    Joined:
    Dec 7, 2015
    Posts:
    25
    Would you happen to have any suggestions as to how I'd go about addressing the "hiccup"? I'm still not quite understanding why the rigidbody "stutters" upon resetting the position when I'm using rigidbody.MovePosition.
    I would use rigidbody.position for my controller but the result has been kind of bad with that route after all. Transform.position or Translate even worse.

    Currently scavenging as much as I can, reading up about floating-point origins / origin rebasing and what-not, as well as rigidbodies...
     
  13. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    There are many possible sources for hiccups, especially when rigidbodies are involved. I can't really tell much from the info in this thread. Ideally, if you could prepare a minimal test project (no art - just cubes etc, with your movement code and camera setup) then I could take a look at it.
     
  14. Lucideus

    Lucideus

    Joined:
    Dec 7, 2015
    Posts:
    25
    https://github.com/Lucideux/FloatingOriginStutter Here's a very basic setup with the bare essentials to replicate the problem I'm having.
     
    Gregoryl likes this.
  15. Kreshi

    Kreshi

    Joined:
    Jan 12, 2015
    Posts:
    445
    I managed to get rid of the camera stutter by setting X, Y and Z Damping under the Body-Part of your CinemachineVirtualCamera to 0, 0, 0 :).

    Looks like the camera was affected by the CinemachineVirtualCameras Damping parameter even though OnTargetObjectWarped(..) was being called.
     
  16. Lucideus

    Lucideus

    Joined:
    Dec 7, 2015
    Posts:
    25
    Yes, I said that in the initial post that removing dampening gets rid of it entirely which is why I'm so confused about the whole thing. The same happens if I make a simple script to make the camera smoothly follow the player (if I were to not use cinemachines). I mostly just wanted to confirm that.
     
  17. Kreshi

    Kreshi

    Joined:
    Jan 12, 2015
    Posts:
    445
    Oh sorry I must have skipped that part :oops::D. You know, sometimes it's pretty late when I read and answer posts :D. But yeah, looks definitely like a weird Cinemachine behaviour there. There is no real reason that Damping affects OnTargetObjectWarped for one/some frame/frames so it's probably a bug in the Cinemachine function :).
     
    Last edited: Aug 17, 2022
  18. Lucideus

    Lucideus

    Joined:
    Dec 7, 2015
    Posts:
    25
    Haha, no harm done! I appreciate your responses all the same! :)
     
  19. Kreshi

    Kreshi

    Joined:
    Jan 12, 2015
    Posts:
    445
    Hmm the more I think about it... OnTargetObjectWarped must actually affect damping because the virtual camera is in a "Damped-Position-State" when the OnTargetObjectWarped is executed which means Cinemachine must move the damped-position by the given offset. Maybe Cinemachine resets the damping-transition when OnTargetObjectWarped is executed - leading to the weird stutter effect?
     
  20. Lucideus

    Lucideus

    Joined:
    Dec 7, 2015
    Posts:
    25
    I tried setting the Body dampening to zero, which we know works fine.
    Then I tried to set the player as the Aim target and gave it some Lookahead Time just to see if there was some weird behaviour, and indeed there is. I wasn't entirely sure (and still can't confidently say) if it was the player stuttering or the camera, but this tells me that it should be the camera that's struggling repositioning, I think?

    Is it something to do with the rotation of the camera that acts up when shifting the origin?

    Update: Okay, so I checked it out with the Game Window Guides enabled. Something is weird here. So I tried setting Dampening up to Max on the Body and yep, no stutters. Well, technically there are but it's not visible. Obviously I can't use maxed out dampening, though.

    Another funny discovery, if using Transposer for the Body with Binding Mode set to Simple Follow with World Up without damping, it moves just fine without stuttering despite having a fairly similar appearance as dampening. I don't know what this means, just thought I'd point it out.
     

    Attached Files:

    Last edited: Aug 18, 2022
    Kreshi likes this.
  21. Kreshi

    Kreshi

    Joined:
    Jan 12, 2015
    Posts:
    445
    It's definitely not the player causing the bug but Cinemachines OnTargetObjectWarped not accounting correctly for all possible transition parameters and offsets :). And you are absolutely right when you say it has something to do with the computed rotation. You can easily test this by setting Y-Follow-Offset to 0 while Z-Follow-Offset stays at -10 (and damping stays at 1 for X, Y and Z). As long as you fly straight, you won't recognize any stutter. The moment you fly sideways and the damping affects the position and therefore indirectly the rotation because it has a look-at target, the stutter starts. This is an indication that the bug is within Cinemachines aiming when OnTargetObjectWarped is called.
     
  22. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    One thing that's important to understand: Cinemachine's damping will hiccup if the target's velocity is not smooth. There is nothing Cinemachine can do about that, and it's not a bug. Damping will only work for targets that move and accelerate smoothly. Juddering in the target's movement will make the target judder in the frame.

    One symptom of this is that the judder will disappear if you turn the damping to zero (what actually happens in that case is that the target gets locked in the frame so it seems stable, and the background judders, but that is much less noticeable because you're looking at the target).

    So the effect you're seeing in this project could either be a bug in Cinemachine, or an inconsistency in the player's velocity at the frame when the recentering occurs. I suspect it's the latter, because this usecase has been tested and used many times in Cinemachine, and OnTargetObjectWarped accounts for everything it needs to.

    Looking at the project, I'm suspicious of the recentering code. I'm not sure it's kosher to be manipulating the physics simulation in that manner. I suspect it messes with the RigidBody state, and that makes the player velocity hiccup, and the CM damping makes that hiccup visible.

    A good test would be to re-implement the movement without RigidBodies. My guess is that the hiccup will disappear.
     
  23. Lucideus

    Lucideus

    Joined:
    Dec 7, 2015
    Posts:
    25
    I see. Any suggestions as to how I could address the velocity inconsistencies when dealing with floating origin rebasing? I only see it happening when using a rigidbody's MovePosition, but it's also the only option that seems to work well with interacting with physics when using a kinematic rigidbody.
     
  24. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    I'm not an expert on RigidBodies, so I can't really answer your question without doing some research. I would suggest asking on the physics forum about how to do a floating origin with physics-driven objects.
     
  25. Lucideus

    Lucideus

    Joined:
    Dec 7, 2015
    Posts:
    25
    Thank you for the suggestion! I'll mess around a bit more and see if I can learn a few more things and then (if needed) I'll ask over there! Thanks for the help!
     
  26. Kreshi

    Kreshi

    Joined:
    Jan 12, 2015
    Posts:
    445
    I found a workaround:

    1.) Call:

    Code (CSharp):
    1. Physics.SyncTransforms();
    in DoRecenter right after MoveCinemachines(focus, -_focusPosition); in your FloatingOrigin.cs at line 581.

    2) Change Rigidbody interpolation in the scene to None.
    3) Change the CineMachineBrain Update Method to Fixed Update.
     
  27. Lucideus

    Lucideus

    Joined:
    Dec 7, 2015
    Posts:
    25
    That actually worked really quite well!
    There was some stuttering left but that was easily addressed by changing the Fixed Timestep from 0.02 to 0.01666.

    Will have to test it out some more to see if I can find some quirks but that was an awesome find, thank you, so so much!

    An interesting (good) side-effect to this seems to be that setting the floating origin manager to update continuously now works flawlessly.

    This made my day!
     
    Last edited: Aug 20, 2022
    Kreshi likes this.