Pill man's got a nasty case of clipping into the ground.

Godot Version

Godot v4.5.1-stable-Win64

Question

So hi.
I'm very new to Godot, a friend of mine suggested I give it a shot as I was looking for a new hobby. Everything seemed like it was going fine, stuff I predicted would be easy and things I knew were going to be difficult.

That said I am stumped on this one issue. I have created a simple character, a pill man. It can move using WASD, can jump with space, can follow the mouse to look around, player has a head bobbing animation and it can press shift to crouch... that's where the issue is. My pill man keeps clipping into the terrain I have set up, sometimes it takes spamming shift and space to get out and other times it completely clips through into the void. I have tried replacing the terrain with a thicker .obj and the issue persists, I have tried messing around with the collision shapes and yet to no avail. Here is a copy of my script for the player:

extends CharacterBody3D



const SPEED = 5.0
var crouch_speed = 2.5
const JUMP_VELOCITY = 4.5
var sensivity = 0.003
@onready var camera = %Camera3D
var crouch_height = 0.5
var stand_height = 2
var crouching = false

func _ready() -> void:
	$Head/AnimationPlayer .play("Head Bob")

	Input.mouse_mode = Input.MOUSE_MODE_CAPTURED

func crouch():
	if Input.is_action_just_pressed("crouch"):
		crouching = !crouching
		if crouching:
			$CollisionShape3D.shape.height = crouch_height
		else:
			$CollisionShape3D.shape.height = stand_height

func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventMouseMotion:
		rotate_y(-event.relative.x * sensivity)
		camera.rotate_x(-event.relative.y * sensivity)
		camera.rotation.x = clamp(camera.rotation.x, deg_to_rad(-60), deg_to_rad(70))

func _physics_process(delta: float) -> void:
	# Add the gravity.
	
	crouch()
	
	if not is_on_floor():
		velocity += get_gravity() * delta

	# Handle jump.a
	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var input_dir := Input.get_vector("left", "right", "up", "down")
	var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		if $Head/AnimationPlayer.speed_scale != 2.5: 
			$Head/AnimationPlayer.speed_scale = 2.5
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED
	else:
		if $Head/AnimationPlayer.speed_scale != 0.0: 
			$Head/AnimationPlayer.speed_scale = 0.0
		if !crouching:
			velocity.x = move_toward(velocity.x, 0, SPEED)
			velocity.z = move_toward(velocity.z, 0, SPEED)
		elif crouching:
			velocity.x = move_toward(velocity.x, 0, crouch_speed)
			velocity.z = move_toward(velocity.z, 0, crouch_speed)

	move_and_slide()

func _process(_delta):
	if Input.is_action_just_pressed("escape"):
		get_tree().quit()
	

Apologies if it looks like spaghetti code, It’s my first time coding anything.

- Player Node

- World Node

Any feedback on how I could solve this would be greatly appreciated!

collision shapes typically scale from their center, so when adjusting the height of the shape, also move it vertically to compensate so that its baseline is kept at the same vertical position.

2 Likes

When I create the collision shape I do it via clicking this:

And then creating a Collision shape based on the shape of the Mesh. Since my terrain is uneven by design I’m sure that also plays a part in why my player is clipping but I’ve tried it on a flat cuboid mesh and it still occasionally catches the floor and gets stuck.

For start, try using built-in parametric shape like capsule.
Also enable debug drawing of collision shapes to see what are they actually doing; Debug > Visible Collision Shapes.

Okay so as it turns out I was overcomplicating it by a ton. I just changed var crouch_height = 0.5 to a 1 and it seems to be working now, no more clipping into the floor.