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

Character Rotates Strangely

Discussion in 'Scripting' started by keenanwoodall, Feb 17, 2015.

  1. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    595
    I'm following the tutorial by Eteeski. If I press a combination of keys to make the character walk diagonally (A and W, A and S, W and D, D and S - anything that would make you walk in a direction other than horizontal or vertical) after pressing the keys and letting go, most of the time the character will just turn around and face another direction. I'm not really sure why.
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. [RequireComponent(typeof(CharacterController))]
    6. public class Player : MonoBehaviour
    7. {
    8.     //Public
    9.     public enum ControlType{Global, CameraRelative};
    10.     public ControlType controlType;
    11.  
    12.     public float moveSpeed = 8.0f;
    13.     public float moveSmoothTime = 0.1f;
    14.     public float rotateSpeed = 180.0f;
    15.     public float rotationSmoothTime = 0.15f;
    16.     public float jumpForce = 15.0f;
    17.     public float inAirSmoothTime = 0.5f;
    18.     public float addedJumpTime = 1f;
    19.     public float gravity = -25.0f;
    20.  
    21.     //Private
    22.     private float currentMoveSmoothTime;
    23.     private Quaternion targetRotation;
    24.     private Vector3 input;
    25.     private Vector3 targetInput;
    26.     private Vector3 currentMovement;
    27.     private Vector3 currentRotation;
    28.     private CharacterController controller;
    29.     private Transform playerCamera;
    30.  
    31.     //Hidden and Ref
    32.     private float horVelocity;//ref variable
    33.     private float verVelocity;//ref variable
    34.     private float rotationVelocity;//ref variable
    35.     private Vector3 movementVelocity;
    36.  
    37.     void Start ()
    38.     {
    39.         controller = this.GetComponent<CharacterController>();
    40.         if(controlType == ControlType.CameraRelative)
    41.         {
    42.             if(GameObject.FindGameObjectWithTag("MainCamera") != null)
    43.                 playerCamera = GameObject.FindGameObjectWithTag("MainCamera").transform;
    44.             else
    45.                 Debug.Log("Player couldn't find a game object with the tag 'MainCamera'.\nPlease give the camera the tag 'MainCamera'.");
    46.         }
    47.  
    48.         currentMoveSmoothTime = moveSmoothTime;
    49.     }
    50.  
    51.     void Update ()
    52.     {
    53.         if(controlType == ControlType.Global)
    54.         {
    55.             input.x += Input.GetAxisRaw("Horizontal") * rotateSpeed * Time.deltaTime;
    56.             input.x = Mathx.ClampAngle(input.x);
    57.             input.z = Mathf.SmoothDamp(input.z, Input.GetAxisRaw("Vertical") * moveSpeed, ref verVelocity, moveSmoothTime);
    58.            
    59.             currentMovement = new Vector3(0, currentMovement.y, input.z);
    60.             currentMovement = transform.rotation * currentMovement;
    61.            
    62.             currentRotation.y =  Mathf.SmoothDampAngle(currentRotation.y, input.x, ref horVelocity, rotationSmoothTime);;
    63.             transform.eulerAngles = new Vector3(transform.eulerAngles.x, currentRotation.y, transform.eulerAngles.z);
    64.         }
    65.         else if(controlType == ControlType.CameraRelative)
    66.         {
    67.             input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    68.             if(input.magnitude > 1)
    69.                 input.Normalize();
    70.             targetInput = playerCamera.rotation * input;
    71.             targetInput.y = 0;
    72.             targetInput = targetInput.normalized * input.magnitude;
    73.  
    74.             Vector3 movementTarget = new Vector3(targetInput.x * moveSpeed, currentMovement.y, targetInput.z * moveSpeed);
    75.             currentMovement = Vector3.SmoothDamp(currentMovement, movementTarget, ref movementVelocity, currentMoveSmoothTime);
    76.  
    77.             if(new Vector3(currentMovement.x, 0, currentMovement.z) != Vector3.zero)
    78.             {
    79.                 targetRotation = Quaternion.LookRotation(new Vector3(currentMovement.x , 0, currentMovement.z));
    80.                 transform.rotation = Quaternion.Euler(0, Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation.eulerAngles.y, ref rotationVelocity, rotationSmoothTime), 0);
    81.             }
    82.         }
    83.  
    84.         /*-----Non-ControlType Functionality-----*/
    85.         if(controller.isGrounded)
    86.         {
    87.             currentMoveSmoothTime = moveSmoothTime;
    88.             if(Input.GetButtonDown("Jump"))
    89.                 currentMovement.y = jumpForce;
    90.             else
    91.                 currentMovement.y = 0;
    92.         }
    93.         else
    94.         {
    95.             currentMoveSmoothTime = inAirSmoothTime;
    96.             currentMovement.y += gravity * Time.deltaTime;
    97.         }
    98.         controller.Move(currentMovement * Time.deltaTime);
    99.     }
    100. }
     
  2. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    595
    Ok, so I think I have it figured out. In the previous tutorial everything worked. So that means that whatever I added in the current one has messed up the movement. I t has to be related to changing the currentSmoothTime. I don't know why that would mess anything up though. I commented out the lines below:
    Code (csharp):
    1.  
    2. /*-----Non-ControlType Functionality-----*/
    3.         if(controller.isGrounded)
    4.         {
    5.             //currentMoveSmoothTime = moveSmoothTime; <<This line
    6.             if(Input.GetButtonDown("Jump"))
    7.                 currentMovement.y = jumpForce;
    8.             else
    9.                 currentMovement.y = 0;
    10.         }
    11.         else
    12.         {
    13.             //currentMoveSmoothTime = inAirSmoothTime; <<And this line
    14.             currentMovement.y += gravity * Time.deltaTime;
    15.         }
    16.         controller.Move(currentMovement * Time.deltaTime);
    17.  
    Now it works fine again. I'm not sure why changing the smoothTime would do anything. This is where currentSmoothTime variable is used:
    Code (csharp):
    1.  
    2. Vector3 movementTarget = new Vector3(targetInput.x * moveSpeed, currentMovement.y, targetInput.z * moveSpeed);
    3.             currentMovement = Vector3.SmoothDamp(currentMovement, movementTarget, ref movementVelocity, currentMoveSmoothTime);
     
  3. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    595
    I've noticed that my currentMovement variable (the one created to be used in the Move() function for the CharacterController) doesn't return to zero when movement keys are released. I wonder why? I can't find any reason for it.
     
  4. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    595
    I did some inefficient debugging. I pressed "W" and "D" to go forward and right. Immediately after letting go of the keys, I paused the game. I went frame by frame and looked at the values of currentMovement. They fluctuated long after I had released the keys.
    Here's the values of currentMovement (a Vector3) after releasing the keys. I have the values inside of code tags because I put spaces before and after the x, y, and z values so that it's easier to read. If the numbers aren't in code tags the white space is removed.
    Code (csharp):
    1.  
    2. 0.1272171,          0,        -0.2770031
    3. 0.08671923,        0,        -0.1878689
    4. 0.05909692,       -0.5,     -0.127399
    5. 0.03799305,        0,        -0.08133303
    6. 0.02251299,        0,        -0.04764469
    7. 0.01311048,        0,        -0.02732919
    8. 0.00746072,       -0.5,     -0.01523054
    9. 0.00345819,        0, -       0.006709964
    10. -0.002103838,     0,        -0.001996744
    11. -0.001872985,    -0.5,     -0.001953524
    12. -0.001603025,     0,        -0.001794212
    13. -0.001333846,     0,        -0.001581325
    14. -0.001067918,    -0.5,     -0.001313791
    15. -0.0008308843,   0,        -0.001059079
    16. -0.0006312695,   0,        -0.0008339866
    17. -0.0004729638,   0,       -0.0006421995
    18. -0.0003504258,  -0.5,    -0.0004862229
    19. -0.0002508156,   0,       -0.0003567046
    20. -0.0001731956,   0,       -0.0002538309
    21. -0.0001194942,   0,         000180018
    22. -8.237685e-05,   -0.5,    -0.0001272856
    23. -5.381738e-05,    0,       -8.601558e-05
    24. -3.271548e-05,    0,       -5.49968e-05
    25. -1.967802e-05,   -0.5,   -3.508166e-05
    26. -1.027468e-05,    0,      -2.03769e-05
    27. -3.801061e-06,    0,      -9.979041e-06
    28. -4.791846e-07,    0,      -4.222421e-06
    29. 1.065485e-06,    -0.5,   -1.176908e-06
    30. 1.94907e-06,       0,       7.876155e-07
    31. 2.371696e-06,     0,       1.953149e-06
    32. 2.303802e-06,     0,       2.264552e-06
    33. 2.022888e-06,     0,       2.15833e-06
    34. 1.711776e-06,     0,      1.946132e-06
    35. 1.410203e-06,     0,      1.690839e-06
    36. 1.12142e-06,      -0.5,   1.392348e-06
    37. 8.666128e-07,     0,      1.113148e-06
    38. 6.537228e-07,     0,      8.694574e-07
    39. 4.870117e-07,     0,      6.654582e-07
    40. 3.591702e-07,    -0.5,   5.014663e-07
    41. 2.556846e-07,     0,      3.659536e-07
    42. 1.753561e-07,     0,      2.587811e-07
    43. 1.20203e-07,       0,      1.824986e-07
    44. 8.235627e-08,    -0.5,   1.28387e-07
    45. 5.334685e-08,     0,      8.618864e-08
    46. 3.199648e-08.     0,      5.458439e-08
    47. 1.892553e-08,    -0.5,   3.445102e-08
    48. 9.552535e-09,     0,      1.965401e-08
    49. 3.143691e-09,     0,      9.24511e-09
    50. -7.762765e-11,    0,      3.563303e-09
    51. -1.516592e-09,   -0.5,   6.230469e-10
    52. -2.304152e-09,    0,     -1.238755e-09
    53. -2.644734e-09,    0,     -2.311982e-09
    54. -2.510299e-09,    0,     -2.542907e-09
    55. -2.177041e-09,   -0.5,  -2.370269e-09
    56. -1.823077e-09,    0,     -2.102588e-09
    57. -1.487895e-09,    0,     -1.802995e-09
    58. -1.175567e-09,   -0.5,  -1.472408e-09
    59. -9.025139e-10,    0,     -1.167913e-09
    60.  
    and then my wrist disintegrated after copying and pasting so much. Hopefully this is enough data to see a pattern or reason behind the fluctuations. All of these values were recorded starting just a few frames after I released the keys "W" and "D". Here's the most recent code:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. [RequireComponent(typeof(CharacterController))]
    6. public class Player : MonoBehaviour
    7. {
    8.     //Public
    9.     public enum ControlType{Global, CameraRelative};
    10.     public ControlType controlType;
    11.  
    12.     public float moveSpeed = 8.0f;
    13.     public float moveSmoothTime = 0.1f;
    14.     public float rotateSpeed = 180.0f;
    15.     public float rotationSmoothTime = 0.15f;
    16.     public float jumpForce = 15.0f;
    17.     public float inAirSmoothTime = 0.5f;
    18.     public float addedJumpTime = 0.5f;
    19.     public float gravity = -25.0f;
    20.  
    21.     //Private
    22.     private float currentMoveSmoothTime;
    23.     private float addedJumpTimeRemaining;
    24.     private Quaternion targetRotation;
    25.     private Vector3 input;
    26.     private Vector3 targetInput;
    27.     public Vector3 currentMovement;
    28.     private Vector3 currentRotation;
    29.     private CharacterController controller;
    30.     private Transform playerCamera;
    31.  
    32.     //Hidden and Ref
    33.     private float sideVelocity;//ref variable
    34.     private float forwardVelocity;//ref variable
    35.     private float rotationVelocity;//ref variable
    36.     private Vector3 movementVelocity;//ref variable
    37.  
    38.     void Start ()
    39.     {
    40.         controller = this.GetComponent<CharacterController>();
    41.         if(controlType == ControlType.CameraRelative)
    42.         {
    43.             if(GameObject.FindGameObjectWithTag("MainCamera") != null)
    44.                 playerCamera = GameObject.FindGameObjectWithTag("MainCamera").transform;
    45.             else
    46.                 Debug.Log("Player couldn't find a game object with the tag 'MainCamera'.\nPlease give the camera the tag 'MainCamera'.");
    47.         }
    48.  
    49.         currentMoveSmoothTime = moveSmoothTime;
    50.         addedJumpTimeRemaining = addedJumpTime;
    51.     }
    52.  
    53.     void Update ()
    54.     {
    55.         if(controlType == ControlType.Global)
    56.         {
    57.             input.x += Input.GetAxisRaw("Horizontal") * rotateSpeed * Time.deltaTime;
    58.             input.x = Mathx.ClampAngle(input.x);
    59.             input.z = Mathf.SmoothDamp(input.z, Input.GetAxisRaw("Vertical") * moveSpeed, ref forwardVelocity, moveSmoothTime);
    60.        
    61.             currentMovement = new Vector3(0, currentMovement.y, input.z);
    62.             currentMovement = transform.rotation * currentMovement;
    63.        
    64.             currentRotation.y =  Mathf.SmoothDampAngle(currentRotation.y, input.x, ref sideVelocity, rotationSmoothTime);;
    65.             transform.eulerAngles = new Vector3(transform.eulerAngles.x, currentRotation.y, transform.eulerAngles.z);
    66.         }
    67.         else if(controlType == ControlType.CameraRelative)
    68.         {
    69.             input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    70.             if(input.magnitude > 1)
    71.                 input.Normalize();
    72.             targetInput = playerCamera.rotation * input;
    73.             targetInput.y = 0;
    74.             targetInput.Normalize();
    75.             targetInput *= input.magnitude;
    76.  
    77.             Vector3 movementTarget = new Vector3(targetInput.x * moveSpeed, currentMovement.y, targetInput.z * moveSpeed);
    78.             currentMovement = Vector3.SmoothDamp(currentMovement, movementTarget, ref movementVelocity, currentMoveSmoothTime);
    79.  
    80.             if(new Vector3(currentMovement.x, 0, currentMovement.z) != Vector3.zero)
    81.             {
    82.                 targetRotation.eulerAngles = new Vector3(targetRotation.eulerAngles.x, Mathf.Atan2(currentMovement.x, currentMovement.z) * Mathf.Rad2Deg, targetRotation.eulerAngles.z);
    83.                 transform.rotation = Quaternion.Euler(0, Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation.eulerAngles.y, ref rotationVelocity, rotationSmoothTime), 0);
    84.                 //targetRotation = Quaternion.LookRotation(new Vector3(currentMovement.x , 0, currentMovement.z));
    85.                 //transform.rotation = Quaternion.Euler(0, Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation.eulerAngles.y, ref rotationVelocity, rotationSmoothTime), 0);
    86.             }
    87.         }
    88.  
    89.         /*-----Non-ControlType Functionality-----*/
    90.         if(controller.isGrounded)
    91.         {
    92.             currentMoveSmoothTime = moveSmoothTime;
    93.             addedJumpTimeRemaining = addedJumpTime;
    94.             currentMovement.y = 0;
    95.         }
    96.         else
    97.         {
    98.             currentMoveSmoothTime = inAirSmoothTime;
    99.             addedJumpTimeRemaining -= Time.deltaTime;
    100.             currentMovement.y += gravity * Time.deltaTime;
    101.         }
    102.  
    103.         //The line below cannot be rearranged
    104.         //It has to be called here so that "addedJumpTimeRemaining" can be calculated
    105.         if(Input.GetButtonDown("Jump") && addedJumpTimeRemaining > 0)
    106.         {
    107.             currentMovement.y = jumpForce;
    108.         }
    109.  
    110.         controller.Move(currentMovement * Time.deltaTime);
    111.     }
    112. }
     
    Last edited: Feb 18, 2015
  5. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    Kind of shooting in the dark here, but I think the SmoothDamp function gets a bit unpredictable when changing the smoothTime value. I would probably just keep a queue of the last N target vectors and average them out to get a smoothed vector. Then, you could change the number of vectors you average to change the amount of time to reach the target.

    I found the math behind SmoothDamp posted here:
    http://answers.unity3d.com/questions/24756/formula-behind-smoothdamp.html

    It made my head hurt :confused:
     
    keenanwoodall likes this.
  6. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    595
    Thanks a bunch. I'm going to implement that right now. I'll let you know how it goes.
     
  7. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    595
    Ok, so now the player doesn't have crazy movement! What a relief! I made a new variable, currentMoveSpeed, and now the input is multiplied by that. So now when in the air, the currentMoveSpeed is multiplied by inAirSpeed. When on the ground, it is multiplied by moveSpeed. The problem with that is, when in the air, the velocity is decreased to currentMoveSpeed is multiplied by inAirSpeed. So basically, the players doesn't keep momentum. He just slows down to inAirSpeed the moment he leaves the ground. I'm trying to think of a workaround and I'll post it if I find it. Here's my current code:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. [RequireComponent(typeof(CharacterController))]
    6. public class Player : MonoBehaviour
    7. {
    8.     //Public
    9.     public enum ControlType{Global, CameraRelative};
    10.     public ControlType controlType;
    11.  
    12.     public float moveSpeed = 8.0f;
    13.     public float moveSmoothTime = 0.1f;
    14.     public float rotateSpeed = 180.0f;
    15.     public float rotationSmoothTime = 0.15f;
    16.     public float jumpForce = 15.0f;
    17.     public float inAirSpeed = 0.5f;
    18.     public float addedJumpTime = 0.5f;
    19.     public float gravity = -25.0f;
    20.  
    21.     //Private
    22.     private float currentMoveSpeed;
    23.     private float addedJumpTimeRemaining;
    24.     private Quaternion targetRotation;
    25.     private Vector3 input;
    26.     private Vector3 targetInput;
    27.     private Vector3 currentMovement;
    28.     private Vector3 currentRotation;
    29.     private CharacterController controller;
    30.     private Transform playerCamera;
    31.  
    32.     //Hidden and Ref
    33.     private float sideVelocity;//ref variable
    34.     private float forwardVelocity;//ref variable
    35.     private float rotationVelocity;//ref variable
    36.     private Vector3 movementVelocity;//ref variable
    37.  
    38.     void Start ()
    39.     {
    40.         controller = this.GetComponent<CharacterController>();
    41.         if(controlType == ControlType.CameraRelative)
    42.         {
    43.             if(GameObject.FindGameObjectWithTag("MainCamera") != null)
    44.                 playerCamera = GameObject.FindGameObjectWithTag("MainCamera").transform;
    45.             else
    46.                 Debug.Log("Player couldn't find a game object with the tag 'MainCamera'.\nPlease give the camera the tag 'MainCamera'.");
    47.         }
    48.  
    49.         currentMoveSpeed = moveSpeed;
    50.         addedJumpTimeRemaining = addedJumpTime;
    51.     }
    52.  
    53.     void Update ()
    54.     {
    55.         if(controlType == ControlType.Global)
    56.         {
    57.             input.x += Input.GetAxisRaw("Horizontal") * rotateSpeed * Time.deltaTime;
    58.             input.x = Mathx.ClampAngle(input.x);
    59.             input.z = Mathf.SmoothDamp(input.z, Input.GetAxisRaw("Vertical") * moveSpeed, ref forwardVelocity, moveSmoothTime);
    60.          
    61.             currentMovement = new Vector3(0, currentMovement.y, input.z);
    62.             currentMovement = transform.rotation * currentMovement;
    63.          
    64.             currentRotation.y =  Mathf.SmoothDampAngle(currentRotation.y, input.x, ref sideVelocity, rotationSmoothTime);;
    65.             transform.eulerAngles = new Vector3(transform.eulerAngles.x, currentRotation.y, transform.eulerAngles.z);
    66.         }
    67.         else if(controlType == ControlType.CameraRelative)
    68.         {
    69.             input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    70.             if(input.magnitude > 1)
    71.                 input.Normalize();
    72.             targetInput = playerCamera.rotation * input;
    73.             targetInput.y = 0;
    74.             targetInput.Normalize();
    75.             targetInput *= input.magnitude;
    76.  
    77.             Vector3 movementTarget = new Vector3(targetInput.x * currentMoveSpeed, currentMovement.y, targetInput.z * currentMoveSpeed);
    78.             currentMovement = Vector3.SmoothDamp(currentMovement, movementTarget, ref movementVelocity, moveSmoothTime);
    79.  
    80.             if(new Vector3(currentMovement.x, 0, currentMovement.z) != Vector3.zero)
    81.             {
    82.                 targetRotation.eulerAngles = new Vector3(targetRotation.eulerAngles.x, Mathf.Atan2(currentMovement.x, currentMovement.z) * Mathf.Rad2Deg, targetRotation.eulerAngles.z);
    83.                 transform.rotation = Quaternion.Euler(0, Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation.eulerAngles.y, ref rotationVelocity, rotationSmoothTime), 0);
    84.                 //targetRotation = Quaternion.LookRotation(new Vector3(currentMovement.x , 0, currentMovement.z));
    85.                 //transform.rotation = Quaternion.Euler(0, Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation.eulerAngles.y, ref rotationVelocity, rotationSmoothTime), 0);
    86.             }
    87.         }
    88.  
    89.         /*-----Non-ControlType Functionality-----*/
    90.         if(controller.isGrounded)
    91.         {
    92.             currentMoveSpeed = moveSpeed;
    93.             addedJumpTimeRemaining = addedJumpTime;
    94.             currentMovement.y = 0;
    95.         }
    96.         else
    97.         {
    98.             currentMoveSpeed = inAirSpeed;
    99.             addedJumpTimeRemaining -= Time.deltaTime;
    100.             currentMovement.y += gravity * Time.deltaTime;
    101.         }
    102.  
    103.         //The line below cannot be rearranged
    104.         //It has to be called here so that "addedJumpTimeRemaining" can be calculated
    105.         if(Input.GetButtonDown("Jump") && addedJumpTimeRemaining > 0)
    106.         {
    107.             currentMovement.y = jumpForce;
    108.         }
    109.  
    110.         controller.Move(currentMovement * Time.deltaTime);
    111.     }
    112. }
    I guess it is pretty important to be able to change the goal of the SmoothDampAngle function, because that makes it easy to keep that nice feeling of maintaining velocity.
     
    Last edited: Feb 18, 2015