Search Unity

How do I prevent the player from "air" movement while jumping using "THIS" script?

Discussion in 'Getting Started' started by AIDesigns, Mar 20, 2018.

  1. AIDesigns

    AIDesigns

    Joined:
    Jul 4, 2017
    Posts:
    3
    Hello all,

    I am learning the C# at the moment and the script I am writing is all done by myself but I am NOT that good. In addition, I am comenting my own script the way I understood and if you see an error you can let me know if you will. Again, I am complete newb but here it is.

    This script that I got here is allowing me to move, run and jump. However, while I am standing still and I jump I cannot move in any direction at all (like in real life) and that is what I do want. However, while I am walking or running (using the same script) and I jump my player will stop in the air in an instant (not like in real file). This is what I do NOT want. What I want is to retain the same think when jumping from idle but keep my momentum while walking/running and jumping (like in real life) but withouth being able to move my character left or right or any other direction.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. [RequireComponent(typeof(CharacterController))]
    6. public class PlayerController : MonoBehaviour
    7. {
    8.     [Header("Floats")]
    9.     [SerializeField]
    10.     [Tooltip("This is our player movement speed.")]
    11.     private float movementSpeed = 1.0f;
    12.  
    13.     //Placeholder for jump velocity.
    14.     private float jumpVelocity;
    15.  
    16.     [SerializeField]
    17.     [Tooltip("Force of a jump going upwards.")]
    18.     private float jumpForce = 17.0f;
    19.  
    20.     [SerializeField]
    21.     [Tooltip("Player regular gravity (universal gravity).")]
    22.     private float gravity = -9.8f;
    23.  
    24.     [SerializeField]
    25.     [Tooltip("Force of a jump going downwards (falling).")]
    26.     private float downForce = 24.0f;
    27.  
    28.     //Our CharacterController.
    29.     private CharacterController _characterController;
    30.  
    31.     //Run it at the start.
    32.     private void Start()
    33.     {
    34.         //This will get our CharacterController at the start of the game.
    35.         _characterController = GetComponent<CharacterController>();
    36.     }
    37.  
    38.     //Run it every frame.
    39.     private void Update()
    40.     {
    41.         GetInput();
    42.         PlayerJump();
    43.         PlayerRun();
    44.     }
    45.  
    46.     private void GetInput()
    47.     {
    48.         //If our cursor is locked then we can rotate our character and if not then we cannot.
    49.         if (Cursor.lockState == CursorLockMode.None) return;
    50.  
    51.         //Get the axis input for our character.
    52.         float horizontalAxis = Input.GetAxisRaw("Horizontal") * movementSpeed;
    53.         float verticalAxis = Input.GetAxisRaw("Vertical") * movementSpeed;
    54.  
    55.         //We use vector3 to map out character movement.
    56.         Vector3 movement = new Vector3(horizontalAxis, 0, verticalAxis);
    57.  
    58.         //Clamp the vector3 movement speed to be the same in all directions.
    59.         movement = Vector3.ClampMagnitude(movement, movementSpeed);
    60.  
    61.         //This is our gravity.
    62.         movement.y = gravity;
    63.  
    64.         //Multiply our movement speed with Time.deltaTime to make it frame per second independent.
    65.         movement *= Time.deltaTime;
    66.  
    67.         //Transform the direction in order for our character be able to move (complex math).
    68.         movement = transform.TransformDirection(movement);
    69.  
    70.         //An actual character movement.
    71.         _characterController.Move(movement);
    72.     }
    73.  
    74.     private void PlayerJump()
    75.     {
    76.         //If our cursor is locked then we can rotate our character and if not then we cannot.
    77.         if (Cursor.lockState == CursorLockMode.None) return;
    78.  
    79.         //If our character is grounded then we can execute the following code.
    80.         if (_characterController.isGrounded)
    81.         {
    82.             movementSpeed = 5.0f;
    83.             //Our "jumpVelocity" stays the same and we are not applying the down-force as our character is not jumping yet.
    84.             jumpVelocity = -downForce * Time.deltaTime;
    85.  
    86.             //get the input key/button for the jump.
    87.             if (Input.GetButtonDown("Jump"))
    88.             {
    89.                 //Convert "jumpVelocity" to "jumpForce".
    90.                 jumpVelocity = jumpForce;
    91.             }
    92.         }
    93.         //If our character is not grounded then we can execute the following code.
    94.         else if (!_characterController.isGrounded)
    95.         {
    96.             movementSpeed = 0.0f;
    97.             //Now we are applying our down-force to get our player down as fast as possible.
    98.             jumpVelocity -= downForce * Time.deltaTime;
    99.         }
    100.  
    101.         //Creating a Vector3 to handle the jumping.
    102.         Vector3 moveVector = new Vector3(0, jumpVelocity, 0);
    103.  
    104.         //An actual player jump.
    105.         _characterController.Move(moveVector * Time.deltaTime);
    106.     }
    107.  
    108.     private void PlayerRun()
    109.     {
    110.         //If our cursor is locked then we can rotate our character and if not then we cannot.
    111.         if (Cursor.lockState == CursorLockMode.None) return;
    112.  
    113.         //If our character is grounded then we can execute the following code.
    114.         if (_characterController.isGrounded)
    115.         {
    116.             if (Input.GetKey(KeyCode.LeftShift))
    117.             {
    118.                 movementSpeed = 10.0f;
    119.             }
    120.             else if (Input.GetKeyUp(KeyCode.LeftShift))
    121.             {
    122.                 movementSpeed = 5.0f;
    123.             }
    124.         }
    125.     }
    126. }
    Could anyone kind enough let me know how this would work?

    Thanks!
    Regards,
    AI
     
  2. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    This is great stuff. You are off to a really good start here.

    Here's what I suggest:
    1. Add a "velocity as Vector3" property to your class. This will be the velocity your character is moving (unless it hits an obstacle).
    2. In your Update method, you should update that velocity based on control inputs and gravity, and then end with _characterController.Move(velocity * Time.deltaTime). (This is almost how you're using your local "movement" vector, but not quite... be sure you keep your velocity vector as a velocity, not a displacement.)
    3. Your Jump method then simply needs to set velocity.y to the appropriate upward speed, leaving velocity.x alone.
    You might also find this article (and the associated demo project) useful, especially if you're making a 2D game.
     
    Bill_Martini and AIDesigns like this.
  3. AIDesigns

    AIDesigns

    Joined:
    Jul 4, 2017
    Posts:
    3

    Hello JoeStrout,


    Thank you very much for a nice comment and thank you very much for a quick reply. I am so new to this C# that I am having a hard time to understand what you are saying (hard for me to visualize it in my head).


    You said: "Add a "velocity as Vector3" property to your class.". Does this mean I edit my current "jumpVelocity"/"movementSpeed" or I add a new "velocity" as Vector3 and replace it/added to "movementSpeed"?


    If it is not too much to ask, can you show me the code example of what you are referring to so I can visually see it?

    Thanks again!
    Kind regards,
    AI
     
  4. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I would add it, then update your code to use it, and then go back and see if jumpVelocity and/or movementSpeed are not needed any more (and if so, delete them).

    Fair enough... this is off the top of my head and untested, and there are certainly more improvements that could be made, but:
    Code (CSharp):
    1.     using System.Collections;
    2.     using System.Collections.Generic;
    3.     using UnityEngine;
    4.    
    5.     [RequireComponent(typeof(CharacterController))]
    6.     public class PlayerController : MonoBehaviour
    7.     {
    8.         [Header("Floats")]
    9.         [SerializeField]
    10.         [Tooltip("This is our player movement speed.")]
    11.         private float movementSpeed = 1.0f;
    12.    
    13.         //Placeholder for jump velocity.
    14.         private float jumpVelocity;
    15.    
    16.         [SerializeField]
    17.         [Tooltip("Upward velocity at the start of a jump.")]
    18.         private float jumpVelocity = 17.0f;
    19.    
    20.         [SerializeField]
    21.         [Tooltip("Player regular gravity (universal gravity).")]
    22.         private float gravity = -9.8f;
    23.    
    24.         [SerializeField]
    25.         [Tooltip("Force of a jump going downwards (falling).")]
    26.         private float downForce = 24.0f;
    27.    
    28.         //Our CharacterController.
    29.         private CharacterController _characterController;
    30.  
    31.         // Current inputs from the user.
    32.         private Vector2 input;
    33.    
    34.         // Our current velocity.
    35.         private Vector3 velocity;
    36.    
    37.         //Run it at the start.
    38.         private void Start()
    39.         {
    40.             //This will get our CharacterController at the start of the game.
    41.             _characterController = GetComponent<CharacterController>();
    42.         }
    43.    
    44.         //Run it every frame.
    45.         private void Update()
    46.         {
    47.             // It looks to me like if the cursor isn't locked, you don't want to do anything.
    48.             if (Cursor.lockState == CursorLockMode.None) return;
    49.            
    50.             // Get inputs.
    51.             GetInput();
    52.            
    53.             // Process jumping/falling, and running.
    54.             PlayerJump();
    55.             PlayerRun();
    56.            
    57.             // Here's where we actually move ourselves, based on current velocity.
    58.             _characterController.Move(velocity * Time.deltaTime);
    59.         }
    60.    
    61.         private void GetInput()
    62.         {
    63.             //Get the axis input for our character.
    64.             float horizontalAxis = Input.GetAxisRaw("Horizontal") * movementSpeed;
    65.             float verticalAxis = Input.GetAxisRaw("Vertical") * movementSpeed;
    66.    
    67.             // Just store the input to be used elsewhere.
    68.             input = new Vector2(horizontalAxis, verticalAxis);
    69.         }
    70.    
    71.         private void PlayerJump()
    72.         {
    73.             //If our character is grounded then we can execute the following code.
    74.             if (_characterController.isGrounded)
    75.             {
    76.                 //get the input key/button for the jump.
    77.                 if (Input.GetButtonDown("Jump")) {
    78.                     // Start a jump, by setting an upward velocity.
    79.                     velocity.y = jumpVelocity;
    80.                 } else {
    81.                     // Grounded, and not jumping, so:
    82.                     velocity.y = 0;
    83.                 }
    84.             }
    85.             //If our character is not grounded then we can execute the following code.
    86.             else if (!_characterController.isGrounded)
    87.             {
    88.                 // We're not grounded, so we should fall downwards.
    89.                 //Now we are applying our down-force to get our player down as fast as possible.
    90.                 // (Why he should fall faster than gravity, I don't understand.)
    91.                 velocity.y -= downForce * Time.deltaTime;
    92.             }
    93.         }
    94.    
    95.         private void PlayerRun()
    96.         {
    97.             //If our character is grounded then we can execute the following code.
    98.             if (_characterController.isGrounded)
    99.             {
    100.                 // Adjust movement speed based on use of the Run key.
    101.                 if (Input.GetKey(KeyCode.LeftShift))
    102.                 {
    103.                     movementSpeed = 10.0f;
    104.                 }
    105.                 else if (Input.GetKeyUp(KeyCode.LeftShift))
    106.                 {
    107.                     movementSpeed = 5.0f;
    108.                 }
    109.                
    110.                 // Now, run at that speed (with just a little bit of smoothing).
    111.                 float targetVx = movementSpeed * input.x;
    112.                 velocity.x = Mathf.MoveTowards(velocity.x, targetVx, 10*Time.deltaTime);
    113.             }
    114.         }
    115.     }
    116.  
    So if you'll forgive some general advice... the key thing to keeping your code organized is to have each method do only one thing. You had the GetInput method both getting inputs, and moving the character, who was also moved in the Jump and Run methods. In my version, GetInput only gets the inputs, and stores them in an "input" property. (I'm not sure why we're bothering to get the vertical axis, since it doesn't seem to be used, but I stored it anyway.)

    Then the PlayerJump method handles jumping and falling. Perhaps it should only handle starting a jump, but I see how falling is at least related. But note that it doesn't move the character, either; it only updates the velocity vector.

    Similarly, the PlayerRun method updates the velocity vector when you're grounded, according to that input vector and a movement speed set according to the Shift key.

    Then, the Update method itself actually calls Move, based on that velocity vector. (Note that since CharacterController is a physics-based thing, you should probably be using FixedUpdate rather than Update here.)

    Hopefully that gives you a few ideas... please play with it, and make it your own!