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

Smooth Motion Without Changing Fixed Timestep

Discussion in 'Scripting' started by AlexandreTarek, Feb 8, 2021.

  1. AlexandreTarek

    AlexandreTarek

    Joined:
    May 13, 2020
    Posts:
    2
    I started learning to use Unity and C# scripting a few weeks ago and I have a hard time achieving smooth movement for my player object in a 3D environment using physics.

    I am using 3 game objects (player, camera, focal point) and 2 scripts (player movement and camera movement). My main camera is a child object of a focal point empty object. The camera movement script is attached to the focal point object. My player and focal point objects use rigidbodies with interpolation.

    My goal is to have a 3rd person camera following an object that can move freely on the x and z axis ( + space bar for additional impulse), and rotates on the y axis based on focal point/camera rotation.

    This is my code for the focal point/camera movement:
    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class MoveCamera : MonoBehaviour
    7. {
    8.     private GameObject player;
    9.     private Rigidbody camRb;
    10.     private float speed;
    11.  
    12.     private void Start()
    13.     {
    14.         // Assign references/variable
    15.         player = GameObject.Find("Player");
    16.         camRb = GetComponent<Rigidbody>();
    17.         speed = 200;
    18.     }
    19.  
    20.     void FixedUpdate()
    21.     {
    22.         RotateCamera();
    23.     }
    24.  
    25.     private void LateUpdate()
    26.     {
    27.         FollowPlayer();
    28.     }
    29.  
    30.     void RotateCamera()
    31.     {
    32.         // Get horizontal axis and rotate on input
    33.         float horizontalInput = Input.GetAxis("Horizontal");
    34.         Quaternion targetRot = Quaternion.AngleAxis(horizontalInput * speed * Time.deltaTime, Vector3.up);
    35.         camRb.rotation *= targetRot;
    36.         // or transform.Rotate(Vector3.up, horizontalInput * speed * Time.deltaTime); ?
    37.     }
    38.  
    39.     void FollowPlayer()
    40.     {
    41.         Vector3 velocity = Vector3.zero;
    42.         transform.position = Vector3.SmoothDamp(transform.position, player.transform.position, ref velocity, 0.02f);
    43.     }
    44. }
    45.  
    And this is my code for the player movement:
    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class PlayerController : MonoBehaviour
    7. {
    8.     private Rigidbody playerRb;
    9.     private GameObject focalPoint;
    10.     private float speed;
    11.     private float boostStrength;
    12.     private bool boostInput;
    13.  
    14.     void Start()
    15.     {
    16.         // Assign references/variables
    17.         playerRb = GetComponent<Rigidbody>();
    18.         focalPoint = GameObject.Find("Focal Point");
    19.         speed = 100;
    20.         boostStrength = 100;
    21.     }
    22.  
    23.     void Update()
    24.     {
    25.         // Check for boost input
    26.         if (Input.GetKeyDown(KeyCode.Space))
    27.         {
    28.             boostInput = true;
    29.         }
    30.     }
    31.  
    32.     void FixedUpdate()
    33.     {
    34.         ForwardMovement();
    35.     }
    36.  
    37.     void ForwardMovement()
    38.     {
    39.         // Move towards focal point on vertical input
    40.         float verticalInput = Input.GetAxis("Vertical");
    41.         playerRb.AddForce(focalPoint.transform.forward * verticalInput * speed);
    42.  
    43.         // Temporary boost on space bar
    44.         if (boostInput)
    45.         {
    46.             playerRb.AddForce(focalPoint.transform.forward * verticalInput * boostStrength, ForceMode.Impulse);
    47.             boostInput = false;
    48.         }
    49.  
    50.         // Smooth rotation based on camera rotation and horizontal input - THIS IS WHERE THINGS DON'T GO AS EXPECTED
    51.         float horizontalInput = Input.GetAxis("Horizontal");
    52.         float velocity = 0.0f;
    53.         float targetRotation = Mathf.Atan2(horizontalInput / 3, verticalInput) * Mathf.Rad2Deg + focalPoint.transform.eulerAngles.y;
    54.         targetRotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref velocity, 0.02f);
    55.         Quaternion targetRot = Quaternion.AngleAxis(targetRotation, Vector3.up);
    56.         playerRb.MoveRotation(targetRot);
    57.     }
    58.  
    For simplicity's sake, let's assume that last part was something like this instead:
    Code (CSharp):
    1. // Rotation based on camera rotation
    2.         float cameraY = focalPoint.transform.rotation.eulerAngles.y;
    3.         transform.localRotation = Quaternion.Euler(0, cameraY, 0);
    4. }
    5.  
    These last two lines of codes are what I was using when I noticed that although the player's forward motion was smooth, the rotation motion (which is based on focal point/camera rotation) stuttered.

    I tried to find solutions to this issue by reading the scripting API and checking out forum threads, and learned a lot. I tried several bits of code that I thought would help smooth out movement, including rigidbody.MoveRotation, Slerp, SmoothDamp, RotateTowards, FromToRotation, LookRotation, etc.
    These gave varied and interesting results, but everything I tried had the same stuttering effect to some extent.

    I then read about execution order, timesteps and the differences between Update(), FixedUpdate() and LateUpdate(). I tried to apply what I learned in my scripts by using Update() to detect user input, LateUpdate() for the FollowPlayer() camera function, and FixedUpdate() for the actual movement.

    However, this didn't solve my problem. To be clearer, everything runs smoothly, except the rotation of the player game object.

    Now, I changed the fixed timestep from 0.02 to 0.01, and all motion is smooth (enough). However, I read that changing the fixed timestep was not usually recommended, as it could affect performance.

    So, here comes my actual question: are there ways to achieve smooth motion without changing the fixed timestep? how do more experienced coders deal with such issues?

    I'm realising now that this post is rather long, so thank you so much for taking the time to read it up to this point!
    (Also, this is my first time posting and english is not my mother tongue, so don't hesitate to mention incorrect format/language)
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,336
    Is the rotation of the player unsmooth? Or just the rotation of the camera following?

    If it is only the camera you're having an issue with, consider installing Cinemachine and letting that take care of it.

    Cinemachine is a package from Unity and it has a lot of features.
     
  3. Stoicheia

    Stoicheia

    Joined:
    Jun 25, 2020
    Posts:
    27
    You are rotating the camera based on user input and setting the player rotation to match rotation of the camera, which is a rather unusual way to implement player rotation. Is your camera movement smooth (as opposed to the player movement)? If so, then if you made horizontal input rotate the player instead of the camera, player rotation would be smooth. Then you can control your camera however you want.
     
  4. AlexandreTarek

    AlexandreTarek

    Joined:
    May 13, 2020
    Posts:
    2
    Thank you for your reply!
    The camera movement is smooth, only the player object jitters when rotating.
    Transferring the horizontal input and rotation to the player movement script seems to only reverse the problem (the player is smoother, but the camera and the rendered scene jitter as the camera follows the player).
    However, I tried your solution in another empty scene with new objects and scripts, and it seemed to work just fine, so I'm guessing it might actually have another problem somewhere, like an optimization issue or something like that.

    Thank you, I will check it out !
     
  5. Stoicheia

    Stoicheia

    Joined:
    Jun 25, 2020
    Posts:
    27
    I guess my main point was that if you let input control the player instead of the camera, you can control the camera however you want, so you can manually go in and add smoothing for example (which may be inappropriate to put on the player), or even have a fixed camera, or a top down view where the camera moves but doesn't rotate, anything you want really. I guess it's more of a design problem than a technical problem.

    Remember, the camera simply captures the game world. The player object has no business knowing where the camera is, or even the fact that it exists.