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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Resolved Is it possible to keep the player object's rotation facing forward when moving backwards?

Discussion in 'Scripting' started by TheJudgementalCat, Jun 16, 2022.

  1. TheJudgementalCat

    TheJudgementalCat

    Joined:
    Jun 12, 2022
    Posts:
    3
    I am trying to get it so when the player is moving backwards the player object does not rotate to be the same as the movement vector but to have the facing vector opposite of the movement vector. See image below (circled in yellow):



    Currently this is what I have for my player movement:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class PlayerControls : MonoBehaviour
    4. {
    5.     [SerializeField]
    6.     private Transform _Camera;
    7.     private CharacterController controller;
    8.     private float verticalVelocity;
    9.     private float groundedTimer;        // to allow jumping when going down ramps
    10.     private float playerSpeed = 5f;
    11.     private float jumpHeight = 1.0f;
    12.     private float gravityValue = 9.81f;
    13.     private float turnSmoothTime = 0.1f;
    14.     private float turnSmoothVelocity;
    15.     private Vector3 moveDir;
    16.     private Quaternion rotation;
    17.     private void Start()
    18.     {
    19.         // always add a controller
    20.         controller = gameObject.AddComponent<CharacterController>();
    21.         moveDir = new Vector3();
    22.     }
    23.     void Update()
    24.     {
    25.         bool groundedPlayer = controller.isGrounded;
    26.         if (groundedPlayer)
    27.         {
    28.             // cooldown interval to allow reliable jumping even whem coming down ramps
    29.             groundedTimer = 0.2f;
    30.         }
    31.         if (groundedTimer > 0)
    32.         {
    33.             groundedTimer -= Time.deltaTime;
    34.         }
    35.         // slam into the ground
    36.         if (groundedPlayer && verticalVelocity < 0)
    37.         {
    38.             // hit ground
    39.             verticalVelocity = 0f;
    40.         }
    41.         // apply gravity always, to let us track down ramps properly
    42.         verticalVelocity -= gravityValue * Time.deltaTime;
    43.         // gather lateral input control
    44.         Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    45.         // scale by speed
    46.         move *= playerSpeed;
    47.  
    48.  
    49.         // Rotates the player based on input and camera via Quaterion;
    50.  
    51.         float cameraAngle = _Camera.eulerAngles.y;
    52.         float playerMovementAngle = Mathf.Atan2(move.x, move.z) * Mathf.Rad2Deg;
    53.         float angle = playerMovementAngle + cameraAngle;
    54.         rotation = Quaternion.Euler(0f, angle, 0f);
    55.        
    56.  
    57.         // only align to motion if we are providing enough input
    58.         if (move.magnitude > 0.05f)
    59.         {
    60.             //Move player in the direction of the current rotation
    61.             moveDir = rotation * Vector3.forward;
    62.             move = moveDir;
    63.         }
    64.         // allow jump as long as the player is on the ground
    65.         if (Input.GetButtonDown("Jump"))
    66.         {
    67.             // must have been grounded recently to allow jump
    68.             if (groundedTimer > 0)
    69.             {
    70.                 // no more until we recontact ground
    71.                 groundedTimer = 0;
    72.                 // Physics dynamics formula for calculating jump up velocity bas/Ded on height and gravity
    73.                 verticalVelocity += Mathf.Sqrt(jumpHeight * 2 * gravityValue);
    74.             }
    75.         }
    76.         // inject Y velocity before we use it
    77.         move.y = verticalVelocity;
    78.         // call .Move() once only
    79.         controller.Move(move * Time.deltaTime * playerSpeed);
    80.     }
    81. }
    82.  
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,970
    I think I know what you want:

    You want to use the dot product (gotten via Vector2.Dot() ) to find out if your player's forward vector and the proposed move vector are facing the same hemisphere.

    Dot returns positive numbers if the two vectors ARE in the same hemisphere, or negative if they are not, eg they are reverse.

    So that means...

    IF you have a valid nonzero input:
    -- get the dot product
    -- perform the movement
    ---- if positive, face the player the way the movement is
    ---- if negative, face the player the opposite way the movement is

    I think I achieved this as a quick test in my Character controller here:

    Magic sauce is lines 65 to line 83... see the comment for what it was
    versus how it does what I list above.

    Code (csharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. // Originally from Unity examples at:
    6. // https://docs.unity3d.com/ScriptReference/CharacterController.Move.html
    7. //
    8. // 3:55 PM 10/3/2020
    9. //
    10. // Reworked by @kurtdekker so that it jumps reliably in modern Unity versions.
    11. //
    12. // To use:
    13. //    - make your player shape about 1x2x1 in size
    14. //    - put this script on the root of it
    15. //
    16. // That's it.
    17.  
    18. public class UnityExampleCharMover : MonoBehaviour
    19. {
    20.     private CharacterController controller;
    21.     private float verticalVelocity;
    22.     private float groundedTimer;        // to allow jumping when going down ramps
    23.     private float playerSpeed = 2.0f;
    24.     private float jumpHeight = 1.0f;
    25.     private float gravityValue = 9.81f;
    26.  
    27.     private void Start()
    28.     {
    29.         // always add a controller
    30.         controller = gameObject.AddComponent<CharacterController>();
    31.     }
    32.  
    33.     void Update()
    34.     {
    35.         bool groundedPlayer = controller.isGrounded;
    36.         if (groundedPlayer)
    37.         {
    38.             // cooldown interval to allow reliable jumping even whem coming down ramps
    39.             groundedTimer = 0.2f;
    40.         }
    41.         if (groundedTimer > 0)
    42.         {
    43.             groundedTimer -= Time.deltaTime;
    44.         }
    45.  
    46.         // slam into the ground
    47.         if (groundedPlayer && verticalVelocity < 0)
    48.         {
    49.             // hit ground
    50.             verticalVelocity = 0f;
    51.         }
    52.  
    53.         // apply gravity always, to let us track down ramps properly
    54.         verticalVelocity -= gravityValue * Time.deltaTime;
    55.  
    56.         // gather lateral input control
    57.         Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    58.  
    59.         // scale by speed
    60.         move *= playerSpeed;
    61.  
    62.         // only align to motion if we are providing enough input
    63.         if (move.magnitude > 0.15f)
    64.         {
    65.             // this was formerly simply:
    66.             //    gameObject.transform.forward = move;
    67.             // to align us to movement.
    68.             //
    69.             // 9:10 PM 6/15/2022 - special hack to let player walk backwards
    70.             //
    71.             Vector3 fwd = transform.forward;
    72.  
    73.             // use the 0.5f through arccos for the 30-degree demarcation
    74.             // angle past which we will consider you moving backwards.
    75.             if (Vector3.Dot( move.normalized, fwd) < -0.5f)
    76.             {
    77.                 // walking backwards
    78.                 gameObject.transform.forward = -move;
    79.             }
    80.             else
    81.             {
    82.                 gameObject.transform.forward = move;
    83.             }
    84.         }
    85.  
    86.         // allow jump as long as the player is on the ground
    87.         // (Change this to Input.GetButton("Jump") if you do not
    88.         //    want the user to need to re-press Jump each time.
    89.         //    Doing so will allow you to hold the Jump button.)
    90.         if (Input.GetButtonDown("Jump"))
    91.         {
    92.             // must have been grounded recently to allow jump
    93.             if (groundedTimer > 0)
    94.             {
    95.                 // no more until we recontact ground
    96.                 groundedTimer = 0;
    97.  
    98.                 // Physics dynamics formula for calculating jump up velocity based on height and gravity
    99.                 verticalVelocity += Mathf.Sqrt(jumpHeight * 2 * gravityValue);
    100.             }
    101.         }
    102.  
    103.         // inject Y velocity before we use it
    104.         move.y = verticalVelocity;
    105.  
    106.         // call .Move() once only
    107.         controller.Move(move * Time.deltaTime);
    108.     }
    109. }[/code\]
     
    Bunny83 likes this.
  3. TheJudgementalCat

    TheJudgementalCat

    Joined:
    Jun 12, 2022
    Posts:
    3
    Now given that I am making a 3d game, I believe I would want to use Quaternion.Dot(), right?
     
  4. karliss_coldwild

    karliss_coldwild

    Joined:
    Oct 1, 2020
    Posts:
    530
    No. You should still use Vector3.Dot. You where are already given example code for 3d character controller. Also I don't see in your code any part that would actually change rotation of player. I assume that is done by different script.

    Few minor unrelated issues that you might want to fix:
    • First multiplication by player speed is useless since you later overwrite it with normalized vector
    • Second multiplication by playerSpeed happens after adding vertical velocity. Which means that you will multiply falling speed by playerSpeed which is nonsense and mess up jump hight calculation.
    • Calculating player angle from vector just to turn it back to vector are unnecessary convoluted way of doing that. You can just rotate input vector by camera angle to achieve camera relative movement.
     
  5. TheJudgementalCat

    TheJudgementalCat

    Joined:
    Jun 12, 2022
    Posts:
    3
    I realized when I uploaded it did not have the line transform.rotation = rotation; which is what currently turns the player based on where the camera is.
     
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,572
    Just in case you did not realise, this line in Kurt's code does rotate your player:
    Code (CSharp):
    1. gameObject.transform.forward = move;
    . Setting the forward axis is equivalent to

    Code (CSharp):
    1. transform.rotation = Quaternion.LookRotation(move, Vector3.up);
     
    Kurt-Dekker likes this.