Search Unity

Resolved How to correctly setup 3d Character Movement in Unity?

Discussion in 'Scripting' started by OfficialTractic, Oct 3, 2020.

  1. OfficialTractic

    OfficialTractic

    Joined:
    Jan 6, 2020
    Posts:
    8
    Hey there, so I'm a fairly new user on Unity and just started to test out the engine and C#.

    I have a question regarding Character Movement in Unity.

    I've used this specific code found from the Documentation:
    https://docs.unity3d.com/ScriptReference/CharacterController.Move.html

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Example : MonoBehaviour
    6. {
    7.    private CharacterController controller;
    8.    private Vector3 playerVelocity;
    9.    private bool groundedPlayer;
    10.    private float playerSpeed = 2.0f;
    11.    private float jumpHeight = 1.0f;
    12.    private float gravityValue = -9.81f;
    13.  
    14.     private void Start()
    15.    {
    16.        controller = gameObject.AddComponent<CharacterController>();
    17.    }
    18.  
    19.     void Update()
    20.    {
    21.        groundedPlayer = controller.isGrounded;
    22.        if (groundedPlayer && playerVelocity.y < 0)
    23.        {
    24.            playerVelocity.y = 0f;
    25.        }
    26.  
    27.         Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    28.        controller.Move(move * Time.deltaTime * playerSpeed);
    29.  
    30.         if (move != Vector3.zero)
    31.        {
    32.            gameObject.transform.forward = move;
    33.        }
    34.  
    35.         // Changes the height position of the player..
    36.        if (Input.GetButtonDown("Jump") && groundedPlayer)
    37.        {
    38.            playerVelocity.y += Mathf.Sqrt(jumpHeight * -3.0f * gravityValue);
    39.        }
    40.  
    41.         playerVelocity.y += gravityValue * Time.deltaTime;
    42.        controller.Move(playerVelocity * Time.deltaTime);
    43.    }
    44. }

    The movement works just fine; however, the jumping absolutely barely works. Sometimes it does and sometimes it does not jump. Could be a cooldown or something, not sure, I'm still a fairly new user.

    Any tips or fixes for this?
     
    Last edited: Oct 4, 2020
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,710
    Welcome!

    First, please use code tags: https://forum.unity.com/threads/using-code-tags-properly.143875/ when you post code.

    But secondly, what is weird is that this is the second time recently that I've seen this code reported broken.

    I went and investigated and what appears to have happened is that Unity's example used to work back on earlier versions of Unity.

    BUT... on more recent Unity versions, I concur with you that it presently does NOT work reliably, at least for jumping.

    If I had to guess why, I believe it is their two separate calls to .Move() interfering with ground sensing in some way.

    I have fixed the script here, and for me it is now 100% reliable, plus I fixed the jump height calculations, and let you jump reliably coming down ramps. See script below. Let me know if it works for you. You'll be the second person running it after me. :)

    I will report it broken on their website, and meanwhile, enclosed is a fully-operational package including a jumpable miniature scene to get you started.

    The main fixes I made are:

    - to only call .Move() once per frame, with a fully-fleshed out velocity instead of twice the way you are above (and the way the Unity sample shows)

    - to let "on ground" linger momentarily after you are not on the ground, to facilitate jumping even when you are "stumbling" down a ramp, which the original code failed on.

    Meanwhile, ENJOY!

    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.05f)
    64.         {
    65.             gameObject.transform.forward = move;
    66.         }
    67.  
    68.         // allow jump as long as the player is on the ground
    69.         if (Input.GetButtonDown("Jump"))
    70.         {
    71.             // must have been grounded recently to allow jump
    72.             if (groundedTimer > 0)
    73.             {
    74.                 // no more until we recontact ground
    75.                 groundedTimer = 0;
    76.  
    77.                 // Physics dynamics formula for calculating jump up velocity based on height and gravity
    78.                 verticalVelocity += Mathf.Sqrt(jumpHeight * 2 * gravityValue);
    79.             }
    80.         }
    81.  
    82.         // inject Y velocity before we use it
    83.         move.y = verticalVelocity;
    84.  
    85.         // call .Move() once only
    86.         controller.Move(move * Time.deltaTime);
    87.     }
    88. }
    EDIT: to jump continuously whenever you re-touch the ground (i.e., not have to keep spamming Jump), change line 69 above to GetButton() instead of GetButtonDown()
     

    Attached Files:

    Last edited: Aug 6, 2021
    libbycb, DMorock, PVisser and 8 others like this.
  3. OfficialTractic

    OfficialTractic

    Joined:
    Jan 6, 2020
    Posts:
    8
    Much appreciated for the help,
    I'm still new here and I thought that looking through Unity's documentation should work just fine, I guess not.

    Anyways, I shall try it out and will give you a reply whenever I try it.

    Thank you once again.
     
    Kurt-Dekker likes this.
  4. OfficialTractic

    OfficialTractic

    Joined:
    Jan 6, 2020
    Posts:
    8
    I've tested it out and I can confirm that now it works properly.

    I hope that I haven't took much of your time, thank you a lot.
     
    Kurt-Dekker likes this.
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,710
    You're welcome! I notified Unity, and I hope they fix the example, because now I have seen three people who have had issues with it in the past week!
     
    OfficialTractic likes this.
  6. OfficialTractic

    OfficialTractic

    Joined:
    Jan 6, 2020
    Posts:
    8
    Hopefully they will.
     
  7. Mikeramczyk1977

    Mikeramczyk1977

    Joined:
    Jul 12, 2020
    Posts:
    58
    Hey Kurt, Could you please help me out here, I am trying to integrate this into the New Input Manager System and that requires me to modify it obviously, so I have but the only issue is that I can't get the character to Jump, it keeps giving me a Null reference. Is there any way you could please help me with this code, I would greatly appreciate it, also, since this is the new input system in Unity, I am sure this question may arise a few times in the near future. Thanks A Million.

    NullReferenceException: Object reference not set to an instance of an object
    CharacterControllerJump.Update () (at Assets/CharacterControllerJump.cs:60)


    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.InputSystem;
    6.  
    7. public class CharacterControllerJump : MonoBehaviour
    8. {
    9.     // Pulls in the Character Controller and sets all the parameters for the character controller
    10.    
    11.     public AudioSource jumpSound;
    12.     public bool isGrounded1;
    13.     public float NumberJumps = 0f;
    14.     public float MaxJumps = 2;
    15.     public float gravityValue = 10f;
    16.     public float jumpHeight = 10.0f;
    17.     private CharacterController controller;
    18.     private float verticalVelocity;
    19.     private float playerSpeed = 2.0f;
    20.  
    21.     //The Main Name of my Action Map Asset  //// and rhen a Generic Name I gave for this script, it can be anything.
    22.     XRIDefaultInputActions input_Actions;
    23.  
    24.  
    25.  
    26.     void update()
    27.     {
    28.         // slam into the ground
    29.         if (isGrounded1 && verticalVelocity < 0)
    30.         {
    31.             // hit ground
    32.             verticalVelocity = 0f;
    33.         }
    34.  
    35.         // apply gravity always, to let us track down ramps properly
    36.         verticalVelocity -= gravityValue * Time.deltaTime;
    37.  
    38.         // gather lateral input control
    39.         Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    40.  
    41.         // scale by speed
    42.         move *= playerSpeed;
    43.  
    44.         // only align to motion if we are providing enough input
    45.         if (move.magnitude > 0.05f)
    46.         {
    47.             gameObject.transform.forward = move;
    48.         }
    49.  
    50.         // inject Y velocity before we use it
    51.         move.y = verticalVelocity;
    52.         // call .Move() once only
    53.         controller.Move(move * Time.deltaTime);
    54.  
    55.  
    56.     }
    57.     private void Update()
    58.     {
    59.         Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    60.         move.y = verticalVelocity;
    61.         controller.Move(move * Time.deltaTime);
    62.         // scale by speed
    63.         move *= playerSpeed;
    64.  
    65.         if (move.magnitude > 0.05f)
    66.         {
    67.             gameObject.transform.forward = move;
    68.         }
    69.     }
    70.     private void Start()
    71.     {
    72.         controller = gameObject.AddComponent<CharacterController>();
    73.     }
    74.  
    75.     private void Awake()
    76.     {
    77.         //Generic Name from above = new "Name of my Action Map Asset"
    78.         input_Actions = new XRIDefaultInputActions();
    79.         input_Actions.XRIRightHand.Jump.performed += x => Jump();
    80.        
    81.     }
    82.  
    83.     //This calls the Jump function from my Action Map Asset.
    84.     private void Jump()
    85.     {
    86.  
    87.         if (isGrounded1)
    88.         {
    89.             //plays a Jump Sound
    90.             jumpSound.Play();
    91.             Debug.Log("Player is Grounded");
    92.             //To test if Button is being heard by the New Input System
    93.             Debug.Log("I AM JUMPING BITCH!!!");
    94.             verticalVelocity += Mathf.Sqrt(jumpHeight * 2 * gravityValue);
    95.  
    96.         }
    97.  
    98.        
    99.     }
    100.  
    101.  
    102.     void OnCollisionEnter(Collision other)
    103.     {
    104.  
    105.  
    106.  
    107.  
    108.  
    109.         isGrounded1 = true;
    110.         NumberJumps = 0;
    111.  
    112.     }
    113.  
    114.     void OnCollisionExit(Collision other)
    115.     {
    116.  
    117.     }
    118.  
    119.     //This Enables and Disables the Method.
    120.  
    121.     #region - Enable/Disable -
    122.  
    123.     private void OnEnable()
    124.     {
    125.         input_Actions.Enable();
    126.     }
    127.  
    128.     private void OnDisable()
    129.     {
    130.         input_Actions.Disable();
    131.     }
    132.  
    133.     #endregion
    134. }
    135.  
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,710
    The answer is always the same... ALWAYS. It is the single most common error ever.

    Don't waste your life spinning around and round on this error. Instead, learn how to fix it fast... it's EASY!!

    Some notes on how to fix a NullReferenceException error in Unity3D
    - also known as: Unassigned Reference Exception
    - also known as: Missing Reference Exception
    - also known as: Object reference not set to an instance of an object

    http://plbm.com/?p=221

    The basic steps outlined above are:
    - Identify what is null
    - Identify why it is null
    - Fix that.

    Expect to see this error a LOT. It's easily the most common thing to do when working. Learn how to fix it rapidly. It's easy. See the above link for more tips.

    This is the kind of mindset and thinking process you need to bring to this problem:

    https://forum.unity.com/threads/why-do-my-music-ignore-the-sliders.993849/#post-6453695

    Step by step, break it down, find the problem.
     
  9. Mikeramczyk1977

    Mikeramczyk1977

    Joined:
    Jul 12, 2020
    Posts:
    58
    Hey Kurt, Could you please help me out here, I am trying to integrate this into the New Input Manager System and that requires me to modify it obviously, so I have but the only issue is that I can't get the character to Jump, it keeps giving me a Null reference. Is there any way you could please help me with this code, I would greatly appreciate it, also, since this is the new input system in Unity, I am sure this question may arise a few times in the near future. Thanks A Million.

    NullReferenceException: Object reference not set to an instance of an object
    CharacterControllerJump.Update () (at Assets/CharacterControllerJump.cs:60)


    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.InputSystem;
    6.  
    7. public class CharacterControllerJump : MonoBehaviour
    8. {
    9.     // Pulls in the Character Controller and sets all the parameters for the character controller
    10.  
    11.     public AudioSource jumpSound;
    12.     public bool isGrounded1;
    13.     public float NumberJumps = 0f;
    14.     public float MaxJumps = 2;
    15.     public float gravityValue = 10f;
    16.     public float jumpHeight = 10.0f;
    17.     private CharacterController controller;
    18.     private float verticalVelocity;
    19.     private float playerSpeed = 2.0f;
    20.  
    21.     //The Main Name of my Action Map Asset  //// and rhen a Generic Name I gave for this script, it can be anything.
    22.     XRIDefaultInputActions input_Actions;
    23.  
    24.  
    25.  
    26.     void update()
    27.     {
    28.         // slam into the ground
    29.         if (isGrounded1 && verticalVelocity < 0)
    30.         {
    31.             // hit ground
    32.             verticalVelocity = 0f;
    33.         }
    34.  
    35.         // apply gravity always, to let us track down ramps properly
    36.         verticalVelocity -= gravityValue * Time.deltaTime;
    37.  
    38.         // gather lateral input control
    39.         Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    40.  
    41.         // scale by speed
    42.         move *= playerSpeed;
    43.  
    44.         // only align to motion if we are providing enough input
    45.         if (move.magnitude > 0.05f)
    46.         {
    47.             gameObject.transform.forward = move;
    48.         }
    49.  
    50.         // inject Y velocity before we use it
    51.         move.y = verticalVelocity;
    52.         // call .Move() once only
    53.         controller.Move(move * Time.deltaTime);
    54.  
    55.  
    56.     }
    57.     private void Update()
    58.     {
    59.         Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    60.         move.y = verticalVelocity;
    61.         controller.Move(move * Time.deltaTime);
    62.         // scale by speed
    63.         move *= playerSpeed;
    64.  
    65.         if (move.magnitude > 0.05f)
    66.         {
    67.             gameObject.transform.forward = move;
    68.         }
    69.     }
    70.     private void Start()
    71.     {
    72.         controller = gameObject.AddComponent<CharacterController>();
    73.     }
    74.  
    75.     private void Awake()
    76.     {
    77.         //Generic Name from above = new "Name of my Action Map Asset"
    78.         input_Actions = new XRIDefaultInputActions();
    79.         input_Actions.XRIRightHand.Jump.performed += x => Jump();
    80.      
    81.     }
    82.  
    83.     //This calls the Jump function from my Action Map Asset.
    84.     private void Jump()
    85.     {
    86.  
    87.         if (isGrounded1)
    88.         {
    89.             //plays a Jump Sound
    90.             jumpSound.Play();
    91.             Debug.Log("Player is Grounded");
    92.             //To test if Button is being heard by the New Input System
    93.             Debug.Log("I AM JUMPING BITCH!!!");
    94.             verticalVelocity += Mathf.Sqrt(jumpHeight * 2 * gravityValue);
    95.  
    96.         }
    97.  
    98.      
    99.     }
    100.  
    101.  
    102.     void OnCollisionEnter(Collision other)
    103.     {
    104.  
    105.  
    106.  
    107.  
    108.  
    109.         isGrounded1 = true;
    110.         NumberJumps = 0;
    111.  
    112.     }
    113.  
    114.     void OnCollisionExit(Collision other)
    115.     {
    116.  
    117.     }
    118.  
    119.     //This Enables and Disables the Method.
    120.  
    121.     #region - Enable/Disable -
    122.  
    123.     private void OnEnable()
    124.     {
    125.         input_Actions.Enable();
    126.     }
    127.  
    128.     private void OnDisable()
    129.     {
    130.         input_Actions.Disable();
    131.     }
    132.  
    133.     #endregion
    134. }
    135.  
     
  10. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,710
    Please stop responding to the same old thread with unrelated nullref issues. START YOUR OWN NEW TOPIC.

    I linked above how to fix a nullref. Nullrefs are NOT related to this post. Nullrefs do NOT need a post at all. See the above for how to fix them.
     
  11. Huxii

    Huxii

    Joined:
    Sep 15, 2018
    Posts:
    37
    Hello @Kurt-Dekker, I am trying to implement your script, but as a result of the script, when falling down I fall too fast, much faster as I actually should with gravity. Did you experience also this behavior? You also mentioned that you have reported it to Unity. Have you opened a bug for that? Would by nice if Unity could solve the issue in the first place.
    Thank you, Huxi
     
  12. bco2135

    bco2135

    Joined:
    Apr 28, 2022
    Posts:
    2
    Can you run "Controller.Move" and "Controller.SimpleMove();" in the same frame?
     
  13. TSRajesh

    TSRajesh

    Joined:
    Jun 19, 2013
    Posts:
    69
    Don't.