CharacterController Jumping/Gravity

This code is for a simple CharacterController which has the ability to move forward, backward, left, and right at various speeds. It is also supposed to have the ability to jump and be affected by gravity.

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(CharacterController))]

public class Controller : MonoBehaviour {

    public float moveSpeed;
    public float backSpeed;
    public float sideSpeed;
    private float currentSpeed = 0;
    public float jumpHeight;

    protected Vector3 move = Vector3.zero;
    protected Vector3 gravity = Vector3.zero;
    protected bool jump = false;

    CharacterController cc;

    // Use this for initialization
    void Start()
    {
        cc = GetComponent<CharacterController>();
    }

    // Update is called once per frame
    void Update()
    {
        if (!cc.isGrounded)
        {
            gravity += Physics.gravity * Time.deltaTime;
        }
        else
        {
            gravity = Vector3.zero;

            if (Input.GetKeyDown(KeyCode.Space))
            {
                jump = true;
            }

            if (jump)
            {
                gravity.y = jumpHeight;
                jump = false;
            }
        }

        if (Input.GetAxis ("Vertical") > 0)
        {
            currentSpeed = moveSpeed;
        }
        else
        {
            currentSpeed = backSpeed;
        }

        var tempMove = Vector3.zero;
        tempMove.y = Input.GetAxis("Vertical") * currentSpeed;
        tempMove.x = -Input.GetAxis("Horizontal") * sideSpeed;
        tempMove.z = gravity.y;

        move = transform.TransformDirection(tempMove);

        cc.Move(move * Time.deltaTime);
    }
}

Any inefficient coding aside, there are basically three issues I’ve been running into:

  • Gravity gets more and more powerful the more times the character falls. When the character becomes ungrounded, his falling speed accelerates until he becomes grounded again. Once becoming ungrounded a second time, he will continue at the same accelerated pace from before, getting even faster the more times he falls.

  • The character does not jump when the spacebar is pressed.

  • The character walks faster when walking diagonally (up key and left/right key depressed) than forward (up key depressed).

Everything else works perfectly. The character moves forward/backward and side to side just fine. I read somewhere that to fix the diagonal issue I should be normalizing the horizontal and vertical floats but I can’t figure out where, and wouldn’t that mess up the jumping/gravity (if either of those even worked properly)?

Have you set a value for jumpHeight in the inspector?
You could try getting the axis values without the speed multiplier & find the velocity. If it is >1 then normalise it. Then multiply the x, y & z values by the speeds.

the += expanded means this:

gravity = gravity + Physics.gravity * Time.deltaTime;

Thanks! I think since the directional values can be negative, Normalize(); wouldn’t work for all four diagonal directions, so I ended up going with something like this:

        var tempMove = Vector3.zero;
        tempMove.y = Input.GetAxis("Vertical");
        tempMove.x = -Input.GetAxis("Horizontal");
        if (tempMove.y == 1 && tempMove.x == -1)
        {
            tempMove.y = 0.7071f;
            tempMove.x = -0.7071f;
        }
        else if (tempMove.y == -1 && tempMove.x == -1)
        {
            tempMove.y = -0.7071f;
            tempMove.x = -0.7071f;
        }
        else if (tempMove.y == 1 && tempMove.x == 1)
        {
           tempMove.y = 0.7071f;
           tempMove.x = 0.7071f;
        }
        else if (tempMove.y == -1 && tempMove.x == 1)
        {
           tempMove.y = -0.7071f;
           tempMove.x = 0.7071f;
        }

        tempMove.y *= moveSpeed;
        tempMove.x *= sideSpeed;
        tempMove.z = gravity.y;

It doesn’t look pretty, but at least the diagonal problem is fixed. Also, jumpHeight is defined in the inspector at a very noticeable value.

As far as the gravity/jumping situation, I have new insight. It appears to be that isGrounded is never becoming true, even when the character is clearly touching a plane. This would explain why everything is going wrong; the player can’t jump and gravity will never reset unless he is grounded. What kinds of things could be causing isGrounded not to be true?

As a test out a tag on the ground. On the character put an OnTriggerEnter that checks if they are on The ground & sets the bool to true with an OnTriggerExit check that sets it to false. Not the most efficient way but it should help you check if the jumping then works. Just check your jumpHeight because if you’ve put it up massively high previously you may leave the game area once you get isGrounded working.

OnTriggerEnter/OnTriggerExit fixed the issue with jumping and confirmed that the only problem is that isGrounded is never being reached.

The ground in my world is a plane with a default mesh collider. Shouldn’t this be making contact with the CharacterController and causing isGrounded to turn true? Is there maybe some alternate solution than setting all my floors to triggers with a “ground” tag?

I think I figured out what the issue is. I believe isGrounded is never reaching true because there is no downward movement at that exact moment where I check the value of isGrounded. I tried adding the same check during a LateUpdate, as well as another Move call, and finally my character was able to jump.

This is what I used:

void LateUpdate()
    {
        if (cc.isGrounded)
        {
            gravity = Vector3.zero;
           
            if (Input.GetKeyDown(KeyCode.Space))
            {
                jump = true;
            }
           
            if (jump)
            {
                gravity.y = jumpHeight;
                jump = false;
            }
        }
        cc.Move(move * Time.deltaTime);
    }

The only problem is, doing this duplicates all the movements that my character performs. How could I reorganize my code so maybe I wouldn’t have to use a LateUpdate and my movements aren’t duplicated?

when you first start the game try setting isGrounded either to true or false because your gravity is being applied manually dependent upon the bool setting so if you start the game with the bool not being set it may not know what to do.

Sweet, I just got it all to work. Thank you so much so your help!!

Idk why, but my Character Controller has built in gravity, why is this?