Handling crouch in multiple circumstances and changing crouch to allow more interaction

Godot Version

4.7

Question

I have done what I think works for me and what should help me from coming across issues later on with a huge list of if , then statements. The issue that I am having currently deals with crouching. I have looked through a few different crouching posts here and I am unsure how to handle my specific issue.

What I know:

I have a crouching statement that changes the characters animation to crouch or to the opposite of it when I hit the crouch button.

I have a ray cast that when it detects collision above it that would stop the character from standing up it forces the crouch to stay as true and won’t let the player stand up.
**
What I can’t resolve:**

I want it setup in a way that if I hit the crouch button while under something that would stop the player from standing I want the player to stand up once they exit the thing they are crouched under.

I know that the ray cast forces crouch to true if collision occurs so that’s what is currently forcing me to stay crouched even if I hit the crouch button again while under something. I know it’s overwritten by the ray cast and is there a way to stop it from doing so. Am I able to have some kind of setup that would allow me to “preemptively” hit the crouch to stand up even while still under something that won’t let me and it stands the player character up once you exit the crouching zone?

Hopefully the above is enough info.

The code:

The code I have for crouching is under the handle_crouching function but i wanted to make sure the entire player character code was provided just in case there is some info I didn’t provide in the above text.

extends CharacterBody2D

@export var movement_speed : float = 100
@export var sprint_speed : float = 200
@export var jump_height :  float = 100

var jump_count : float = 0
var max_jumps : float = 2
var gravity : float = 980
var current_speed : float = 0

var can_jump : bool = false
var crouching : bool = false
var sprinting : bool = false


# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	pass

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _physics_process(delta: float) -> void:
	handle_movement_physics(delta)
	handle_crouching()
	handle_sprinting()
	move_and_slide()

func handle_crouching():
	if Input.is_action_just_pressed("crouch"):
		crouching = !crouching
	
	if $CrouchDetector.is_colliding():
	
		crouching = true
	
	if crouching:
		sprinting = false
		$CrouchedAnimation.show()
		$StandingAnimation.hide()
		$PlayerCollision.scale.y = 0.5
		$PlayerCollision.position.y = 15
	else:
		$StandingAnimation.show()
		$CrouchedAnimation.hide()
		$PlayerCollision.scale.y = 1.0
		$PlayerCollision.position.y = 0

func handle_movement_physics(delta):
	velocity.x = 0
	if not is_on_floor():
		velocity.y += gravity * delta
	else:
		jump_count = 0
	if Input.is_action_pressed("move_left"):
		velocity.x -= current_speed
	if Input.is_action_pressed("move_right"):
		velocity.x += current_speed
	#jumping and controling if the character can jump more than once depending on the jump_count
	if Input.is_action_just_pressed("jump"):
		#if the character is on the floor OR they have less jumps left than the max_jumps then they can still jump.
		if is_on_floor() and jump_count < max_jumps:
			velocity.y = -jump_height
			jump_count += 1

func handle_sprinting():
	current_speed = movement_speed
	if sprinting == true:
		current_speed = sprint_speed
	if Input.is_action_pressed("sprint"):
		sprinting = true

What you need is kind of an input buffer. You need another variable to save the crouch_pressed state to apply it immediately when the player is allowed to uncrouch.

var crouch_pressed: bool = false
var crouching: bool = false

func handle_crouching() -> void:
	if Input.is_action_just_pressed("crouch"):
		##Check if player is currently crouching but can't stand. 
		##If they can't stand, store crouch_pressed in a variable
		if $CrouchDetector.is_colliding():
			if crouching:
				##queue crouch_pressed
				crouch_pressed = true
			return ##If player can't stand, there is no need to do anything after this

		if crouching: ##if currently crouching, stand and set crouching as false
			_uncrouch_player()
		else: ##if standing, crouch and set crouching as true
			_crouch_player()

func _crouch_player() -> void:
	sprinting = false
	$CrouchedAnimation.show()
	$StandingAnimation.hide()
	$PlayerCollision.scale.y = 0.5
	$PlayerCollision.position.y = 15
	crouching = true
	
func _uncrouch_player() -> void:
	$StandingAnimation.show()
	$CrouchedAnimation.hide()
	$PlayerCollision.scale.y = 1.0
	$PlayerCollision.position.y = 0
	crouching = false

func _physics_process(delta: float) -> void:
	##Constantly check if the player is not longer anything and if a crouch_pressed is queued
	if not $CrouchDetector.is_colliding() and crouching and crouch_pressed:
		if crouching:
			crouch_pressed = false ##Remember to reset couch_pressed when the player stands
			_uncrouch_player()

If you were using a state machine, you wouldn’t need the constant check in the physics process.

I believe the above should work in your case. Let me know if it works.

Hi,

Thank you for responding with your suggested changes. I’ll try to make this kind of setup in mine and let you know what happened.

I am limited on time but will get to trying those changes as soon as I am able and let you know if that worked.

I do have a logistics/long-term question. Should I structure my code in a way like this and try to use functions inside functions often like in the code you wrote? I am not super experienced yet so I’m still learning so much.

Another question: How would a state machine help me here? Is that something you would recommend? I am not super familiar with them besides the fact that they set and change from different states through functions inside of a larger node. I could be wrong about that but feel like that’s kinda the basics. (Please correct me if I am wrong here.)

Last question: Are there any major changes you would suggest to my overall code to make it more workable or am I heading in the right direction based on what I have with my little experience. I don’t want to do anything super complex but am willing to learn parts and pieces as needed to make it less terrible and to prevent myself from writing code that hinders me in the long run.

Thanks so much for you help either way. I’m excited to test out your suggestion and learn more :slight_smile: