Search code examples
unity-game-enginegame-physicsunityscript

Unity 3D game jumping randomly


I am developing my first game with the Unity3D engine and I have run into my first problem! Not as exciting as I thought. If I spam the jump button w my ball jumps the first time then when it lands it does not jump right away but it jumps randomly after a number of button presses. The code I am using is below.

    #pragma strict

var rotationSpeed = 100;
var jumpHeight = 8;

private var isFalling = false;

function Update ()
{
    //Handle ball rotation.
    var rotation : float = Input.GetAxis ("Horizontal") * rotationSpeed;
    rotation *= Time.deltaTime;
    rigidbody.AddRelativeTorque (Vector3.back * rotation);

    if (Input.GetKeyDown(KeyCode.W) && isFalling == false)
    {
        rigidbody.velocity.y = jumpHeight;
    }
    isFalling = true;
}

function OnCollisionStay ()
{
    isFalling = false;
}

I heard this was a arithmetic behavior problem in UnityScript. I am a very beginner at programming and do not really understand the code in UnityScript and this is the first game/project I am making in Unity3D. Help would be greatly appreciated. Thanks!


Solution

  • You would think that something as simple as jumping should be a no-brainer, but there are a couple of gotchas here:

    • It can happen that Update() is called twice without any physics updates in between. This means you don't receive OnColliderEnter nor OnColliderStay in between but you still reset isFalling, blocking the jump key.
    • The moment the ball hits you will receive OnColliderEnter, but not OnColliderStay. This won't happen until the next physics update. If you only listen to -stay you will be one update late.
    • If you press jump right at the time the ball is supposed to hit the ground then your key is already down when the first collider hit registers. You will be another update late.
    • Objects sink a little bit into the collider. When you jump, it might take a couple of updates before you clear the collider and you will receive OnColliderStay after you jumped.

    I think that last point is the biggest problem here. To solve this you need to do a couple of things:

    • Use both OnColliderEnter and OnColliderStay. This will imrpove your timing with one update.
    • Reset isFalling in FixedUpdate instead of in Update. FixedUpdate is part of the physics loop, so it is called right before OnCollisionEnter and OnCollisionStay who will set it again immediately if needed.
    • Remember that you are trying to jump until you are actually in the air. This allows you to clear the collider with one button press.

    The code below implements these points. If you want the timing to be even tighter you must queue the next jump while you are in the air.

    #pragma strict
    var rotationSpeed = 100;
    var jumpHeight = 8;
    
    private var isFalling = false;
    private var tryingToJump = false;
    
    function Update ()
    {
        //Handle ball rotation.
        var rotation : float = Input.GetAxis ("Horizontal") * rotationSpeed;
        rotation *= Time.deltaTime;
        rigidbody.AddRelativeTorque (Vector3.back * rotation);
    
        if (Input.GetKeyDown(KeyCode.W) && !isFalling) tryingToJump = true;
        if (tryingToJump)
        {
            if (isFalling) tryingToJump = false;
            else rigidbody.velocity.y = jumpHeight;
        }
    }
    
    function FixedUpdate()
    {
        isFalling = true;
    }
    
    function OnCollisionStay()
    {
        isFalling = false;
    }
    
    function OnCollisionEnter()
    {
        isFalling = false;
    }