1
votes

I am making a game in Unity where you move around with WASD and space to jump. This is my code:

using UnityEngine;

public class PlayerMovement : MonoBehaviour {

    public Rigidbody rb;
    public bool canJump;

    void FixedUpdate () {
        if (Input.GetKey("w"))
        {
            rb.AddForce(0, 0, 750 * Time.deltaTime, ForceMode.Acceleration);
        }

        if (Input.GetKey("a"))
        {
            rb.AddForce(-750 * Time.deltaTime, 0, 0, ForceMode.Acceleration);
        }

        if (Input.GetKey("s"))
        {
            rb.AddForce(0, 0, -750 * Time.deltaTime, ForceMode.Acceleration);
        }

        if (Input.GetKey("d"))
        {
            rb.AddForce(750 * Time.deltaTime, 0, 0, ForceMode.Acceleration);
        }

        if (canJump)
        {
            if (Input.GetKeyDown("space"))
            {
                rb.AddForce(0, 10, 0, ForceMode.VelocityChange);
                Debug.Log("jump");
                canJump = false;
            }
        }

        Vector3 v = rb.velocity;
        v.y = 0;
        v = Vector3.ClampMagnitude(v, 6);
        v.y = rb.velocity.y;
        rb.velocity = v;
    }

    void OnCollisionEnter(Collision other)
    {
        if (other.gameObject.name == "Ground")
        {
            canJump = true;
        }
    }

    void OnCollisionExit(Collision other)
    {
        if (other.gameObject.name == "Ground")
        {
            canJump = false;
        }
    }
}

But, when I jump, it sometimes goes quite high, and sometimes barely jumps at all. I have tested whether holding down the space bar makes any difference, and it doesn't. I have tried using ForceMode.Impulse and ForceMode.VelocityChange, but the jumping is still inconsistent.

I have also noticed that the player only jumps high up in the air when its Y value on the floor is around ~0.23. It can also be ~0.16, like this, and it doesn't jump very high at all.

This is ~0.16 0.16 And this is ~0.23 0.23

The lowest jump takes it up to a Y value of ~0.7, but the highest jump takes it up to around 4.83.

If anyone has any ideas, I'd be very grateful.

2
@AnLog What do you mean by a debug? Do you mean in the jump statement put Debug.Log("jump")? - William Jones
Sorry, I literaly went fiddlefingers on mobile and posted an incomplete comment. I think you should add a Debug.Log("jump") line to the jump section to see if it isn't actually jumping more than once right at the beginning where it's still in contact with the ground. - AnLog
Ok, that's a clever idea. I'll try that. I did look at the internet and it said to add isGrounded = false at the end of the jump statement, so I'll try that too. Thanks! Edit: my code already has isGrounded = false in it, so I'll update the code in the question. - William Jones
@AnLog It appears to only be jumping once. - William Jones

2 Answers

4
votes

What I usually do is I check if I'm going upwards or downwards. I only accept the groundCheck if I go downwards.

void OnCollisionEnter(Collision other)
{
    if (other.gameObject.name == "Ground" && rb.velocity.y < 0)
    {
        canJump = true;
    }
}

However, you use getKeyDOWN so it's not about holding space etc.

So the next thing I can recommend is this: set velocity.y to 0 before jumping.

if (Input.GetKeyDown("space"))
{
    Vector3 vel = rb.velocity;
    vel.y = 0;
    rb.velocity = vel;
    rb.AddForce(0, 10, 0, ForceMode.VelocityChange);
    // ...
}

edit: Wait, my first thought was "he uses getKey and holds space", my 2nd thought was : "he missed Time.deltaTime". But now I see it:

You used Input.Get... in FixedUpdate!

Let me explain:

All Inputs are evaluated before all the Updates are called. As you can see in the execution Order the Input events are handled right before update.

Now depending on your framerate, Update is called often or rarely, compared to the (almost) constant FixedUpdate.

So FixedUpdate can get called multiple times between Update calls. And that means the input events ran once.

SO I have to assume that Input.GetKeyDown(Key.SPACE) will be true in multiple FixedUpdates!

Easy fix:

bool jump_pressed = false;

void Update()
{
    if(Input.GetKeyDown("space"))
    {
        jump_pressed = true;
    }
    if(Input.GetKeyUp("space"))
    {
        jump_pressed = false;
    }
}

void FixedUpdate()
{
    if(jump_pressed)
    {
        jump_pressed = false;
        rb.AddForce(0, 10, 0, ForceMode.VelocityChange);
        // etc...
    }
}

So you just handle all the Input logic in Update, and execute the rest in fixed update.

Of course jump_pressed needs to be set to false in FixedUpdate because it would stay true multiple FixedUpdates otherwise.

edit2: I looked at your code again. Whatever you do, no matter if it's in FixedUpdate or Update, don't use Time.deltaTime in AddForce.

AddForce will manipulate the velocity. And the velocity is used in each PhysicsUpdate to move the transform. But PhysicsUpdate/FixedUpdate tries to run at an (almost) fixed rate, while Time.deltaTime give you the elapsed time of the last (Update) Frame.

If you move a transform by yourself (position +=...) - use Time.deltaTime. But don't use it when you use AddForce.

0
votes

okay if you need this to get urgent fix. Grab the unity default FPS character and explore its properties and components and try to do the same with your own object. You can go into the detail later