Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Visual stutter when Move/Look are in FixedUpdate/Update

Discussion in 'Scripting' started by Braanflakes, Jul 18, 2023.

  1. Braanflakes

    Braanflakes

    Joined:
    Jan 30, 2015
    Posts:
    6
    Apologies as this will be a longer post, but I haven't had much luck googling this issue so I figured I'd make a post here.

    I'm having an issue with my player controller where there appears to be some visual stutter around objects in the scene while moving and looking at the same time, and I'm pretty confident the issue is related to the Update/FixedUpdate loops but I'm not 100% sure.

    I've included a video example below of each case but I'll also explain what I'm seeing.

    Movement and Look logic

    Code (CSharp):
    1.  
    2. private void Look()
    3. {
    4.     float rotation_x = look_input_value.x * mouse_sensitivity_x;
    5.     float rotation_y = look_input_value.y * mouse_sensitivity_y;
    6.  
    7.     if (invert_mouse_y)
    8.     {
    9.         rotation_y = -rotation_y;
    10.     }
    11.  
    12.     target_view += new Vector3(-rotation_y, rotation_x);
    13.     target_view.x = Mathf.Clamp(target_view.x, -90f, 90f);
    14.  
    15.     // Apply rotation to camera, player orientation, and player object itself
    16.     player_camera.transform.rotation = Quaternion.Euler(target_view);
    17.     player_orientation.transform.rotation = Quaternion.Euler(0, target_view.y, 0);
    18.     transform.rotation = Quaternion.Euler(0, target_view.y, 0);
    19.  
    20.     // Move camera with camera position transform
    21.     player_camera.transform.position = camera_position.transform.position;
    22. }
    23.  
    24. private void Move()
    25. {
    26.     // Calculate movement direction
    27.     Vector3 move_direction = player_orientation.forward * move_input_value.y + player_orientation.right * move_input_value.x;
    28.  
    29.     // Apply movement force
    30.     if (is_grounded)
    31.     {
    32.         rb.AddForce(move_direction.normalized * move_speed * move_speed_multiplier, ForceMode.Force);
    33.     }
    34.     else
    35.     {
    36.         rb.AddForce(move_direction.normalized * move_speed * move_speed_multiplier * air_speed_multiplier, ForceMode.Force);
    37.      }
    38. }
    39.  
    40.  
    Case 1: Move() in FixedUpdate and Look() in Update
    There appears to be visual stutter, especially around the edges of objects while both moving and rotating the camera at the same time. The stutter appears to lessen while either moving only or looking only, however it's hard to tell if that's truly the case.

    Code (CSharp):
    1. void Update()
    2. {
    3.     Look();
    4. }
    5.  
    6. void FixedUpdate()
    7. {
    8.     Move();
    9. }


    Case 2: Move() and Look() in FixedUpdate
    The visual stutter appears to go away or at least be lessened by a major degree, however this seems to be an incorrect implementation of Look() as the player doesn't seem to have "true" raw mouse input, and that makes sense when thinking about inputs coming from the mouse. I assume we would want any key/mouse inputs to happen in Update() otherwise something could be missed.

    Code (CSharp):
    1. void FixedUpdate()
    2. {
    3.     Look();
    4.     Move();
    5. }


    Has anyone run into a similar issue? I may just be going about this controller in an entirely incorrect way, so any help/advice is much appreciated!
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,718
    First off - I see you are an Insym viewer because you left your sound on :D

    Second - Basically doing movement or rotation in FixedUpdate is going to end up with this stuttering because your motion is not synced to the framerate.

    But since you are using Rigidbody motion you absolutely want to add your forces in FixedUpdate. The key here is to enable interpolation on the Rigidbody. So overall:
    1. Enable interpolation on your Rigidbody
    2. Do Move() in FixedUpdate
    3. Do Rotate() in Update
     
    MaskedMouse and Braanflakes like this.
  3. Braanflakes

    Braanflakes

    Joined:
    Jan 30, 2015
    Posts:
    6
    Ha, I forgot to strip the sound of that recording, good catch!

    I appreciate the advice. I actually already had interpolation enabled on the player Rigidbody, and it is set to the "Interpolate" option. Maybe I should try Extrapolate instead?

    upload_2023-7-18_9-51-59.png
     
  4. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,718
    I think the final piece of this puzzle is whatever this is:

    camera_position.transform.position


    Where is that coming from?
     
    Braanflakes likes this.
  5. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    510
    I'm guessing your camera isn't a child object of the player...

    If your camera isn't a rigidbody then both its position and rotation should be updated in Update() because it won't be interpolated if updated from FixedUpdate(). This means that if your physics frame rate is 30 and your rendering frame rate is 60 then your camera would only ever be updated at 30 fps, resulting in jitter.

    I notice in your look script that you're setting three different transform rotations. This seems a little odd as we normally only need to set the player rotation and the camera rotation. So I've no idea what that third rotation is.

    Also:

    player_camera.transform.position = camera_position.transform.position;

    I'm not sure what that does but I hope it sets the camera's position to the player's position.
     
    Chubzdoomer and Braanflakes like this.
  6. Braanflakes

    Braanflakes

    Joined:
    Jan 30, 2015
    Posts:
    6
    So camera_position is a reference to the empty gameobject "Camera Position" shown below, that I'm using to store where the camera should be positioned on the player, and I'm updating the main camera's position to match it every update.

    Now that you've mentioned it though, I may be better off just making the camera a child of the player gameobject and simplifying that. I assumed having the camera attached to the player gameobject could cause some odd behavior when applying forces but it may be that updating its position in this way is actually causing more problems.

    upload_2023-7-18_10-46-52.png
     
  7. Braanflakes

    Braanflakes

    Joined:
    Jan 30, 2015
    Posts:
    6
    This makes a ton of sense. It made me think about what exactly I was rotating and it turns out rotating the player gameobject was the cause of the issue. I was thinking ahead to rotating the player's model along with the rotation of the camera, but I'll need to handle that separately down the road.

    If I'm understanding everything correctly, I was rotating the player gameobject that has a rigidbody attached in Update() which should only be happening in FixedUpdate(). I think this, coupled with the position of the rigidbody being correctly updated in FixedUpdate(), resulted in jitter.

    I've included the updated code and a video of the result below. Move() did not change and is still in FixedUpdate() while Look() is in Update().

    Code (CSharp):
    1. private void Look()
    2. {
    3.     float rotation_x = look_input_value.x * mouse_sensitivity_x;
    4.     float rotation_y = look_input_value.y * mouse_sensitivity_y;
    5.  
    6.     if (invert_mouse_y)
    7.     {
    8.         rotation_y = -rotation_y;
    9.     }
    10.  
    11.     target_view += new Vector3(-rotation_y, rotation_x);
    12.     target_view.x = Mathf.Clamp(target_view.x, -90f, 90f);
    13.  
    14.     // Apply rotation to camera, player orientation, and player object itself
    15.     player_camera.transform.rotation = Quaternion.Euler(target_view);
    16.     player_orientation.transform.rotation = Quaternion.Euler(0, target_view.y, 0);
    17.     // transform.rotation = Quaternion.Euler(0, target_view.y, 0);
    18.  
    19.     // Move camera with camera position transform
    20.     player_camera.transform.position = camera_position.transform.position;
    21. }
     
  8. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    510
    I think because you're already setting/moving the camera in Look it should be fine with only Look being in Update() and Move being in FixedUpdate().
     
  9. Braanflakes

    Braanflakes

    Joined:
    Jan 30, 2015
    Posts:
    6
    That makes sense. I went ahead and simplified everything by just attaching the camera directly to the player gameobject and scrapping the update of the position altogether since that will happen by virtue of it being childed. Now only the camera rotation is being updated in Update() instead of three separate transforms.

    Thanks everyone for your help/advice, I really appreciate it!
     
  10. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    510
    I'm normally a proponent of attaching the camera to the player object but I recently did a test to see what happens if I run the physics at 30 fps and my rendering frame rate at 60 fps and I found that I had considerable stutter. I then detached the camera and everything became smooth again.

    So if you're creating an application that's destined for a platform that requires a physics frame rate that's much lower than the rendering frame rate then make sure to test first. You may find that you have to detach the camera again. :)
     
    Braanflakes likes this.