Trouble with crouch function — Player will never stand despite raycast not colliding with anything?

Godot Version

4.6

Question

Bit of a WIP at the moment, but I’m creating a crouch function inside of my character controller and can’t get my player to stand. From what I’ve programmed, it should allow the player to crouch if the “crouch” input action is triggered, and allow them to stand up if their head isn’t colliding with anything. I haven’t been able to get my player to stand back up even if the raycast isn’t hitting anything. I tested with print(head_ray_cast.get_collider()) to check if it was hitting anything and it returned null. Despite this, my code is never printing “stand” or executing any of the associated code.

I’m not super knowledgeable about programming right now, so if anyone can provide some help it would be appreciated.

extends CharacterBody3D


# Pre-load SFX
var footstep_sfx = [
# Dirt
preload("uid://fy4dyo4k4ypm"),
preload("uid://bo6qvh82kp6x7"),
preload("uid://c4k7hij1kp8pw"),
 preload("uid://ckluvbvw0tuue"),
preload("uid://d00nufsw80gvf")]

# Tweakable variables
@export var mouse_sensitivity := 0.005
@export var walk_speed := 50
@export var acceleration_speed := 10
@export var run_speed := 110

# Movement speed
var movement_speed := 50

# Node references
@onready var camera_3d: Camera3D = %Camera3D
@onready var head: Node3D = %Head
@onready var footstep_timeout: Timer = %FootstepTimeout
@onready var footstep_sounds: AudioStreamPlayer3D = %FootstepSounds
@onready var collision_shape_3d: CollisionShape3D = %CollisionShape3D
@onready var head_ray_cast: RayCast3D = $HeadRayCast

func _ready() -> void:
	footstep_timeout.timeout.connect(_footsteps)
	Input.mouse_mode = Input.MOUSE_MODE_CAPTURED

func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventMouseMotion:
		
		camera_3d.rotation.y -= event.relative.x * mouse_sensitivity
		camera_3d.rotation.x -= event.relative.y * mouse_sensitivity
	
		const MAX_ANGLE := PI/2.1
		camera_3d.rotation.x = clampf(camera_3d.rotation.x, -MAX_ANGLE, MAX_ANGLE)
	
func _physics_process(delta: float) -> void:
	var movement_input := Input.get_vector("Left","Right","Forward","Back")
	var player_movement := Vector3(movement_input.x * movement_speed * delta, 0, movement_input.y * movement_speed * delta).rotated(Vector3.UP, camera_3d.global_rotation.y) 
	
	velocity.x = move_toward(velocity.x, player_movement.x, acceleration_speed * delta)
	velocity.z = move_toward(velocity.z, player_movement.z, acceleration_speed * delta)
	move_and_slide()
	
	
func _process(delta: float) -> void:
	sprint()
	crouch()
	print(head_ray_cast.get_collider())
	
func _footsteps() -> void:
	footstep_sfx.shuffle()
	footstep_sounds.stream = footstep_sfx.pick_random()
	if velocity.length() > 0:
		footstep_sounds.play()
		
func crouch() -> void:
	if Input.is_action_pressed("Crouch"):
		collision_shape_3d.shape.height = 1.0
		var crouchcamheight := create_tween()
		crouchcamheight.set_ease(Tween.EASE_IN_OUT)
		crouchcamheight.tween_property(head, "position:y:", -0.5, 0.3 )
		print("crouched ")
	elif not head_ray_cast.is_colliding:
		collision_shape_3d.shape.height = 2.0
		var crouchcamheight := create_tween()
		crouchcamheight.set_ease(Tween.EASE_IN_OUT)
		crouchcamheight.tween_property(head, "position:y:", 0, 0.3 )
		print("standing")

without the parenthesis is_colliding() this elif checks if the function itself does not exist; use parenthesis to call the function.

elif not head_ray_cast.is_colliding():
2 Likes

THANK YOU!! I thought the engine would have caught something like that and told me.

By omitting the parentheses you can access any function as Callable (to store it in a variable or pass it around before calling it). And because this Callable is a valid object, it evaluates to true.

That’s why the engine isn’t complaining about it.

2 Likes

I would love an “Implicit Cast” warning (especially to boolean), which would catch this issue and some others.

1 Like