Search Unity

How to smoothen my player movement script

Discussion in 'Scripting' started by f2pallglory, Aug 2, 2019.

  1. f2pallglory

    f2pallglory

    Joined:
    Jan 11, 2019
    Posts:
    25
    So i have this script as my simple player movement using rigidbody, its movement work just like i wanted
    the problem is the player rotation is instant and doesnt feel smooth at all, how can i make it smoother?
    Im sorry but im rather new at unity so a little explanation could help too.
    Thanks in advance.

    Code (CSharp):
    1.  
    2. public class CharacterController : MonoBehaviour {
    3.     public float speed;
    4.     public Rigidbody body;
    5.     public Vector3 input;
    6.  
    7.     void Start () {
    8.         speed = 10f;
    9.  
    10.     }
    11.    
    12.     void Update () {
    13.  
    14.         input = Vector3.zero;
    15.         input.x = Input.GetAxisRaw("Horizontal");
    16.         input.z = Input.GetAxisRaw("Vertical");
    17.         input = input.normalized;
    18.  
    19.         if (input != Vector3.zero) {
    20.             transform.forward = input;
    21.         }
    22.     }
    23.  
    24.     private void FixedUpdate()
    25.     {
    26.         body.MovePosition(body.position + input * speed * Time.fixedDeltaTime);
    27.     }
    28.  
    29. }
    30.  
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    Line 20 is causing your problem, as you are instantly jamming your input into the transform.forward.

    You can use the .MoveRotation() function of your Rigidbody instance to instead turn to a particular angle (heading).

    To get that heading, you can use Mathf.Atan2() on the input.x/y, which will give you the result in radians.

    You can use Mathf.Rad2Deg to convert that result to degrees, which lets you use it in Quaternion.Euler(0,0,myDegreeAngle) to produce a new rotation to put into .MoveRotation()

    NOW... having done all that, it is STILL going to be instantaneous. But keep reading!

    The good news is that with the heading variable:

    - you can keep the previous heading as a member variable
    - you are computing the new heading above
    - you can tween slowly from previous to current in whatever manner you like.

    Lotta steps, but there you have it.

    NOTE: Depending on how you implement it, it will have specific characteristics, such as do you only tween to the new heading while any input is present? Does the object move in the intervening-tweened direction too? (i.e., curve around to go the other way). All of these details will come out as you implement it, but by working from a heading you can control all of them explicitly.
     
  3. f2pallglory

    f2pallglory

    Joined:
    Jan 11, 2019
    Posts:
    25
    Thankyou for answering, so i tried to use rigidbody.MoveRotation as you explained above, but looks like i got something wrong cause my player cant rotate to 180 (behind) and while its walking it cant change rotation like when i make input become transform.forward
    this is my code :
    Code (CSharp):
    1. public class CharacterController : MonoBehaviour {
    2.     public float speed;
    3.     public Rigidbody body;
    4.     public Vector3 input;
    5.     float heading;
    6.     Quaternion target;
    7.  
    8.     void Start () {
    9.         speed = 2f;
    10.     }
    11.  
    12.     void Update () {
    13.  
    14.         input = Vector3.zero;
    15.         input.x = Input.GetAxisRaw("Horizontal");
    16.         input.z = Input.GetAxisRaw("Vertical");
    17.         input = input.normalized;
    18.         heading = (Mathf.Atan2(input.x, input.y)) * Mathf.Rad2Deg;
    19.         target = Quaternion.Euler(0,heading, 0);
    20.         target = target.normalized;
    21.  
    22.         if (input != Vector3.zero) {
    23.          
    24.             body.MoveRotation(target);
    25.          
    26.         }
    27.  
    28.     }
    29.  
    30.     private void FixedUpdate()
    31.     {
    32.         body.MovePosition(body.position + input * speed * Time.fixedDeltaTime);
    33.      
    34.     }
    35.  
    36. }
    37.  
    38.  
     
    anycolourulike and Kurt-Dekker like this.
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    I think your problem is that line 15 and 16 you are feeding .x and .z, but on 18 you are using .x and .y for Atan2...

    I apologize because I actually told you to use x/y but I didn't look closely enough at your code, and in any case I actually meant "the two orthogonal axes along which your player is walking." My bad. I think if you fix that it will be happier.
     
    anycolourulike and f2pallglory like this.
  5. f2pallglory

    f2pallglory

    Joined:
    Jan 11, 2019
    Posts:
    25
    Yeah it works really well after i changed it, then i dont know if its the best way or not but i lerp the target and the base rotation now it moves smoothly like i wanted too
    Thankyou so much :)

    Code (CSharp):
    1. public class CharacterController : MonoBehaviour {
    2.     public float speed;
    3.     public float rotSpeed;
    4.     public Rigidbody body;
    5.     public Vector3 input;
    6.     public Animator ani;
    7.     float heading;
    8.     Quaternion target;
    9.     Quaternion baseRot;
    10.  
    11.     void Start () {
    12.         speed = 1.5f;
    13.         rotSpeed = 10f;
    14.     }
    15.  
    16.     void Update () {
    17.  
    18.         input = Vector3.zero;
    19.         input.x = Input.GetAxisRaw("Horizontal");
    20.         input.z = Input.GetAxisRaw("Vertical");
    21.         input = input.normalized;
    22.         baseRot = body.rotation;
    23.         heading = (Mathf.Atan2(input.x, input.z)) * Mathf.Rad2Deg;
    24.         target = Quaternion.Euler(0, heading ,0 );
    25.         target = target.normalized;
    26.  
    27.         if (input != Vector3.zero) {
    28.             body.MoveRotation(Quaternion.Lerp(baseRot, target, Time.deltaTime * rotSpeed));
    29.         }
    30.      
    31.     }
    32.  
    33.     private void FixedUpdate()
    34.     {
    35.         body.MovePosition(body.position + input * speed * Time.fixedDeltaTime);
    36.     }
    37.  
    38. }
     
    anycolourulike and Kurt-Dekker like this.
  6. fct509

    fct509

    Joined:
    Aug 15, 2018
    Posts:
    108
    I don't know if anyone has pointed this out yet, but you really should do a zero check before you normalize or you could get some nasty errors. Also, it might a good idea not to normalize until you get an input that's outside the [-1, +1] range or the sum of each input's squared value is above 1.0. Something like:

    Code (CSharp):
    1.  
    2. input.x = Input.GetAxisRaw("Horizontal");
    3. input.z = Input.GetAxisRaw("Vertical");
    4. if (1 < input.sqrMagnitude)
    5. {
    6.     input = input.normalized;
    7. }
    8.  
    The code above will only normalize when you get a vector with a magnitude that's greater than one while clamping the inputs to the [-1, +1] range.

    Besides that:

    • It looks like your code will cause an item to get up to full speed within a single frame without any speed-up (or slowdown). While this is really responsive, it's not what I would call smooth.
    • You might want to look at the timings for the project, I've noticed that a lot of projects will have a slower FixedUpdate() than an Update(), so you might be updating a few things more often than necessary if you only need them in the FixedUpdate().
    • Also, if you really want things to run smoothly, I would also store a prevInput value that's filled in with the input value that was used in the previous FixedUpdate(). Then I would average the prevInput with the current input value and multiply the resulting value by the speed and delta-time. While this isn't perfect, it will smooth out movement even more (especially during drops in fps).

    Code (CSharp):
    1.  
    2. {
    3.     body.MovePosition(body.position + Vector3.Lerp(input, prevInput, 0.5f) * speed * Time.fixedDeltaTime);
    4.     prevInput = input;
    5. }
    6.  
    Hope this helps.