Unity character movement behaving weirdly. What is going on?

I have gotton my character to move and jump. For some reason when I land from a jump I then move in pulses. The movement is a start then stop and so on after. It doen’t happen when I change the play speed to certain speeds but does when I change it to other certain speeds. For example with a speed of 5 it is fine unless I jump into one of the red things which just have capsule round collision. However at seed 6 it does the weird movement when I land after jumping. When the weird movement is happening I cannot jump. I have tested and it is running the jump code.


using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem.XR;

public class PlayerMovement : MonoBehaviour
{
    Rigidbody rb;
    [SerializeField] float MovementSpeed = 5.0f;
    [SerializeField] float JumpInitialVelocity = 5.0f;

    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    // Update is called once per frame
    void Update()
    {
        Vector3 NewPlayerVelocity = Vector3.zero;
        Vector3 CurrentVelocity = rb.velocity;   //Players current velocity before update

        //This input code need the unity input manager

        //
        //Jump movement

        if (Input.GetKeyDown(KeyCode.Space))  //The down on Get Key Down means that it is only true on pressed and you must release it to trigger it again
        {
            Debug.Log("hi");
            if (rb.velocity.y == 0) //If vertical velocity is 0 then player is on ground so player can only jump when on the ground
            {
                rb.velocity = new Vector3(rb.velocity.x, JumpInitialVelocity, rb.velocity.z);
            }
        }

        //Bellow is WASD movement input converted into game player movement
        else if (rb.velocity.y == 0) //If vertical velocity is 0 then player is on ground so player can only move when on the ground
        {
            
            //
            //Forward and back movement

            if (!Input.GetKey(KeyCode.W) && !Input.GetKey(KeyCode.S))   //If W and S are both not pressed then stop forward and back movement if on ground
            {
                if (rb.velocity.y == 0)
                {
                    rb.velocity = new Vector3(rb.velocity.x, rb.velocity.y, 0);
                }

            }
            else if (Input.GetKey(KeyCode.W) && Input.GetKey(KeyCode.S))    //If W and S are both being pressed then stop forward and back movement if on ground
            {
                rb.velocity = new Vector3(rb.velocity.x, rb.velocity.y, 0);
            }
            else    //If none of the above is true then move as normal
            {
                if (Input.GetKey(KeyCode.W))
                {
                    rb.velocity = new Vector3(rb.velocity.x, rb.velocity.y, MovementSpeed);
                }

                if (Input.GetKey(KeyCode.S))
                {
                    rb.velocity = new Vector3(rb.velocity.x, rb.velocity.y, -MovementSpeed);
                }
            }

            //
            //Left and right movement

            if (!Input.GetKey(KeyCode.A) && !Input.GetKey(KeyCode.D))   //If A and D are both not pressed then stop forward and back movement if on ground
            {
                if (rb.velocity.y == 0)
                {
                    rb.velocity = new Vector3(0, rb.velocity.y, rb.velocity.z);
                }
            }
            else if (Input.GetKey(KeyCode.A) && Input.GetKey(KeyCode.D))    //If W and S are both being pressed then stop forward and back movement if on ground
            {
                rb.velocity = new Vector3(0, rb.velocity.y, rb.velocity.z);
            }
            else    //If none of the above is true then move as normal
            {
                if (Input.GetKey(KeyCode.A))
                {
                    rb.velocity = new Vector3(-MovementSpeed, rb.velocity.y, rb.velocity.z);
                }

                if (Input.GetKey(KeyCode.D))
                {
                    rb.velocity = new Vector3(MovementSpeed, rb.velocity.y, rb.velocity.z);
                }
            }
        }
    }
}

The movement is a little dificult to explain. It only moves for a second then stops then repeats this and it stops me jumping.I think this is something to do with the collision but I don’t know. Does anyone know what is going on here?

I think your ‘grounded’ detection is not stable. I don’t believe it’s a good idea to check if the character is grounded by checking if its y-velocity is 0. Any movement using physics is likely to affect the y-velocity very slightly. You could try

if (Mathf.Approximately(rb.velocity.y, 0f))
{
}

but I suspect the y-velocity may even exceed Epsilon and then this may still not work. You may have to find a different way to detect grounded.

Some ways I’ve seen before is casting a short (length <= 0.01f) ray from the feet of the character and seeing if it hits the terrain. Another is having a trigger at the feet and setting/clearing a flag when the terrain enters/exits the trigger.

And after all this, this may not even be your problem…

I did ray casts to the ground and found that the ground is always 0.01168017 away from the bottom of the player unless the weird movement is happening. If weird movement is happening it is futher away by about 0.00000030 that number varies a bit.

if (Mathf.Approximately(rb.velocity.y, 0f))
{
}

I’m not sure how to use the code above. But it seems to always fail when the weird movement is happening and also fail while in the air.
What do you make of this?

Mathf.Approximately checks if the values almost match but the range is 2x float.Epsilon which is the smallest value a float can have that is not 0. So, instead of checking whether or not the rb.velocity.y is 0 (or approximately 0) you can set a threshold. Perhaps make a function to check

private bool IsGrounded(float distanceToGround, float threshold)
{
    return (distanceToGround <= threshold && distanceToGround >= -threshold);
}

This is roughly the same thing that Mathf.Approximately does, but allows you to set the threshold instead of using float.Epsilon.

In your code you have several of these kinda checks

if (rb.velocity.y == 0) //If vertical velocity is 0 then player is on ground so player can only jump when on the ground

Replace the if with the new function

if (IsGrounded(distanceToGround, 0.0125f))

I have used 0.0125f as the threshold here because that is what your testing suggested it should be. The character will then be considered ‘grounded’ if the distance to the ground is between -0.0125 and 0.0125. This should be enough. You can use the result of your raycast as the distanceToGround

1 Like

Thank you. That works perfectly and I understand now why a non zero affected the movement. I failed to realise that my WASD only was executed if player is grounded. Thanks for the help.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms