Raycasts wont register in time if player has high enough velocity

Hey there, hope someone here smarter than me can figure out my mistake.
I have a simple platformer game using raycasts to check if you’re standing on the ground. I tried colliders, but started using raycasts to be able to jump just before the player collides and thereby losing his x-velocity to friction, which would be a problem to how the game is supposed to be played.

That is exactly what happens. If you fall fast enough, then the x-velocity turns to 0 instantly instead of the player jumping just before collision and retaining his x-velocity.

It seems my raycasts do not check in time, or something else fails that I just cant figure out. Here’s what I have tried so far by reading other posts complaining about raycasts not registering, all to no different result:

  • Turning on Continous collision detection in my players rigidbody instead of Discrete
  • Putting the raycasts in FixedUpdate (made it worse)
  • Putting my Movement script right after Default Time in the section Script Execution Order in Project Files

Here’s the method in question:

 public void Jump()
    {
        LayerMask ground = LayerMask.GetMask("Ground");

        Vector2 bottomCenter = new Vector2(transform.position.x, transform.position.y - transform.GetComponent<Renderer>().bounds.size.y / 2);
        Vector2 bottomLeftCornerPos = new Vector2(transform.position.x - transform.GetComponent<Renderer>().bounds.size.x / 2, transform.position.y - transform.GetComponent<Renderer>().bounds.size.y / 2);
        Vector2 bottomRightCornerPos = new Vector2(transform.position.x + transform.GetComponent<Renderer>().bounds.size.x / 2, transform.position.y - transform.GetComponent<Renderer>().bounds.size.y / 2);

        // Cast a ray straight down.
        RaycastHit2D down = Physics2D.Raycast(bottomCenter, Vector2.down, 1f, ground);
        RaycastHit2D downleft = Physics2D.Raycast(bottomLeftCornerPos, Vector2.down, 1f, ground);
        RaycastHit2D downright = Physics2D.Raycast(bottomRightCornerPos, Vector2.down, 1f, ground);

        // Determine the closest hit point
        RaycastHit2D hit = down.collider != null ? down : (downleft.collider != null ? downleft : downright);
        if (down.collider != null && (hit.collider == null || down.distance < hit.distance)) hit = down;
        if (downleft.collider != null && (hit.collider == null || downleft.distance < hit.distance)) hit = downleft;
        if (downright.collider != null && (hit.collider == null || downright.distance < hit.distance)) hit = downright;

        // If it hits something...
        if (hit.collider != null)
        {

            if (hit.distance >= 0.01 && hit.distance <= 0.1 && rb2d.velocityY <= 0)
            {

                rb2d.velocityY = jumpPower;
                comboExperiration = ComboExpireTime;
                comboCounter++;


                //Since we are not colliding with floor, send a message to Player Stats that we jumped on a floor
                if (hit.collider.gameObject.CompareTag("Floor"))
                {
                    GetComponent<PlayerStats>().CollisionByJump(hit.collider.gameObject);
                }

            }
        }
    }

Jump() gets called in inputManager script:

void Update()
    {

        if (playerInput.MovementMap.Jump.inProgress)
        {
            player.GetComponent<PlayerMovement>().Jump();
        }

Hope anyone here recognises this problem or can see my wrongings. Thanks in advance

EDIT: This is what I ended up as my solution thanks to Shinyclef’s suggestions. It’s scuffed, but it seems to work without impacting negatively yet. Friction gets disabled as you leave the ground, gets enabled 1 sec after hitting ground. No friction means collision with the ground doesn’t affect the x velocity.

    public void LookForGroundRay()
    {

        if (rb2d.velocityX == 0)
            print("velocity was 0");
        LayerMask ground = LayerMask.GetMask("Ground");

        Vector2 bottomCenter = new Vector2(transform.position.x, transform.position.y - transform.GetComponent<Renderer>().bounds.size.y / 2);
        Vector2 bottomLeftCornerPos = new Vector2(transform.position.x - transform.GetComponent<Renderer>().bounds.size.x / 2, transform.position.y - transform.GetComponent<Renderer>().bounds.size.y / 2);
        Vector2 bottomRightCornerPos = new Vector2(transform.position.x + transform.GetComponent<Renderer>().bounds.size.x / 2, transform.position.y - transform.GetComponent<Renderer>().bounds.size.y / 2);

        // Cast a ray straight down.
        RaycastHit2D down = Physics2D.Raycast(bottomCenter, Vector2.down, 1f, ground);
        RaycastHit2D downleft = Physics2D.Raycast(bottomLeftCornerPos, Vector2.down, 1f, ground);
        RaycastHit2D downright = Physics2D.Raycast(bottomRightCornerPos, Vector2.down, 1f, ground);

        // Determine the closest hit point
        RaycastHit2D hit = down.collider != null ? down : (downleft.collider != null ? downleft : downright);
        if (down.collider != null && (hit.collider == null || down.distance < hit.distance)) hit = down;
        if (downleft.collider != null && (hit.collider == null || downleft.distance < hit.distance)) hit = downleft;
        if (downright.collider != null && (hit.collider == null || downright.distance < hit.distance)) hit = downright;
        storeHit = hit;

        if (storeHit.distance >= 0.01 && storeHit.distance <= 0.2 && rb2d.velocityY <= 0)
        {
            Grounded = true;
            if (GetComponent<Rigidbody2D>().sharedMaterial.friction != 0.4f)
                StartCoroutine(frictionEnable());
        }
        else
            Grounded = false;
    }

    IEnumerator frictionEnable()
    {


        yield return new WaitForSeconds(1f);
        GetComponent<Rigidbody2D>().sharedMaterial.friction = 0.4f;
    }

A quick read tells me you have a raycast window between 0.01 and 0.1. this is how far the ground must be below the character in order for the character to be eligible to ‘air jump’. So yeah if you’re travelling faster than 0.1 per second you can overshoot this window in one frame entirely. Continuous detection has nothing to do with raycasts so it won’t help you.

To make this work, think support the extreme case. What should happen when, in a single frame, you go from 10 units above to landed on the platform?

Maybe you need to raycast from your previous position of y == 10 to your new position plus offset of y == -0.1 and look for a hit like that. If you find a hit, you can jump.

You already be touching the ground though, so maybe after adding y velocity you might want to restore X velocity or something as friction might have come into effect. Alternatively, disable friction until you detect walking again so you don’t have to worry about restoring X velocity.

Those are my immediate thoughts. Perhaps there is a better idea.

Good luck.