Can someone help me implement crouch (2d)

I am trying to implement a crouch function without using a pre-made character controller, but it just won’t work. For some reason I have to press ctrl multiple times for my character to either stand up or crouch, and I don’t get why. I tried searching but it seams not many people program this way.
Here’s my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class move : MonoBehaviour
{

Rigidbody2D rb2d;
SpriteRenderer spriterender;
BoxCollider2D bcol;
bool crouch = false;
[SerializeField] 
private LayerMask platformLayerMask;

// Start is called before the first frame update
void Start()
{
    rb2d = GetComponent<Rigidbody2D>();
    spriterender = GetComponent<SpriteRenderer>();
    bcol = GetComponent<BoxCollider2D>();
}

// Update is called once per frame
void FixedUpdate()
{
    if (crouch == true)
    {
        bcol.enabled = false;
    }
    else if (crouch == false) { bcol.enabled = true; }

    if (Input.GetKey("d") || Input.GetKey("right"))
    {
        rb2d.velocity = new Vector2(4, rb2d.velocity.y);
        spriterender.flipX = false;
    }
    ////////
    else if (Input.GetKey("a") || Input.GetKey("left"))
    {
        rb2d.velocity = new Vector2(-4, rb2d.velocity.y);
        spriterender.flipX = true;
    }
    ////////
    else
    {
        rb2d.velocity = new Vector2(0, rb2d.velocity.y);
    }
    ////////

    if (IsGrounded() && Input.GetKey("space") || Input.GetKey("up") && IsGrounded())
    {
        rb2d.velocity = new Vector2(rb2d.velocity.x, 17);
    }

    if (Input.GetKeyDown(KeyCode.LeftControl))
    {
        crouch = true;
        rb2d.velocity = new Vector2((rb2d.velocity.x) / 2, (rb2d.velocity.y) - 5);
        Debug.Log("crouching");
    }
    if (Input.GetKeyUp(KeyCode.LeftControl))
    {
        crouch = false;
        rb2d.velocity = new Vector2(rb2d.velocity.x, rb2d.velocity.y);
        Debug.Log("standing");
    }

}
   ////////////////

private bool IsGrounded()
{
    RaycastHit2D raycastHit = Physics2D.BoxCast(bcol.bounds.center, bcol.bounds.size, 0f, Vector2.down, 1f, platformLayerMask);
    Color rayColor;
    if (raycastHit.collider != null)
    {
        rayColor = Color.green;
    }
    else
    {
        rayColor = Color.red;
    }
    Debug.DrawRay(bcol.bounds.center + new Vector3(bcol.bounds.extents.x, 0), Vector2.down * (bcol.bounds.extents.y + 1f), rayColor);
    Debug.DrawRay(bcol.bounds.center - new Vector3(bcol.bounds.extents.x, 0), Vector2.down * (bcol.bounds.extents.y + 1f), rayColor);
    Debug.DrawRay(bcol.bounds.center - new Vector3(bcol.bounds.extents.x, bcol.bounds.extents.y, 1f), Vector2.right * (bcol.bounds.extents.y), rayColor);
    return raycastHit.collider != null;
}

}

Small update: Today I tried the same code, and the first time it worked, but then it stopped working for some reason

2 Answers

2
  1. please please please put your actions, state checks and drawing of debugs in different functions. In the code provided it is cumbersome to find anything. It will help you debug it yourself too.
  2. Using Raycasts is a fickle thing to decide whether the character is colliding (constantly), you could try with a onCollisionStay for grounding, but still that would also work on stuff in other normalized directions.
    What I mean is that the RayCast (if set too short, like only 1 or 2 pixels outside the bounds) might miss and hit in several frames. This is why I would put these items in separate functions AND do detection in Update rather than FixedUpdate. (StackOverflow: Update runs once per frame. FixedUpdate can run once, zero, or several times per frame, depending on how many physics frames per second are set in the time settings, and how fast/slow the framerate is.). If you put the isGrounded public in inspector, you will see that the state is constantly changing even when the sprite is ‘steady’.
    Put a delay on the detection to make sure you are ‘grounded’ for several frames. This will help, but first break up your code to represent well formatted.

I will be honest with you. I am still really new to this stuff and didn't understand everything you said, but I did understand a few things and appreciate the criticism. Thanks for the help, I will follow your advise

How do I put a delay in isGrounded?

Just to let you know, I dicided not to put your corrections yet because I would have to rewrite everything, sorry, please don't get mad

Fixed!
Corrected code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class move : MonoBehaviour
{
    Rigidbody2D rb2d;
    SpriteRenderer spriterender;
    BoxCollider2D bcol;
    bool crouch = false;
    [SerializeField] 
    private LayerMask platformLayerMask;


    // Start is called before the first frame update
    void Start()
    {
        rb2d = GetComponent<Rigidbody2D>();
        spriterender = GetComponent<SpriteRenderer>();
        bcol = GetComponent<BoxCollider2D>();
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        if (crouch == true)
        {
            bcol.enabled = false;
        }
        else if (crouch == false) { bcol.enabled = true; }

        if (Input.GetKey("d") || Input.GetKey("right"))
        {
            rb2d.velocity = new Vector2(4, rb2d.velocity.y);
            spriterender.flipX = false;
        }
        ////////
        else if (Input.GetKey("a") || Input.GetKey("left"))
        {
            rb2d.velocity = new Vector2(-4, rb2d.velocity.y);
            spriterender.flipX = true;
        }
        ////////
        else
        {
            rb2d.velocity = new Vector2(0, rb2d.velocity.y);
        }
        ////////

        if (IsGrounded() && Input.GetKey("space") || Input.GetKey("up") && IsGrounded())
        {
            rb2d.velocity = new Vector2(rb2d.velocity.x, 17);
        }

        if (Input.GetKey(KeyCode.LeftControl))
        {
            crouch = true;
            rb2d.velocity = new Vector2((rb2d.velocity.x) / 2, (rb2d.velocity.y) - 5);
            Debug.Log("crouching");
        }else
        {
            crouch = false;
            rb2d.velocity = new Vector2(rb2d.velocity.x, rb2d.velocity.y);
            Debug.Log("standing");
        }

    }
       ////////////////


    private bool IsGrounded()
    {
        RaycastHit2D raycastHit = Physics2D.BoxCast(bcol.bounds.center, bcol.bounds.size, 0f, Vector2.down, 1f, platformLayerMask);
        Color rayColor;
        if (raycastHit.collider != null)
        {
            rayColor = Color.green;
        }
        else
        {
            rayColor = Color.red;
        }
        Debug.DrawRay(bcol.bounds.center + new Vector3(bcol.bounds.extents.x, 0), Vector2.down * (bcol.bounds.extents.y + 1f), rayColor);
        Debug.DrawRay(bcol.bounds.center - new Vector3(bcol.bounds.extents.x, 0), Vector2.down * (bcol.bounds.extents.y + 1f), rayColor);
        Debug.DrawRay(bcol.bounds.center - new Vector3(bcol.bounds.extents.x, bcol.bounds.extents.y, 1f), Vector2.right * (bcol.bounds.extents.y), rayColor);
        return raycastHit.collider != null;
    }

}

Cool, glad you figured it out yourself (because that's the best feeling no doubt)....also I'd recommend converting your comment to an answer and accepting it to facilitate answer for future visitors....and all the best with the game you're making