Can't figure out how to fix wall physics bug

Godot Version

v4.2.2.stable.official [15073afe3]

Question

I have an issue where my 2d platformer player character does not interact nicely with walls when jumping against them.

Please consult this video:

Here is the code for the player character:

Click here to view


extends CharacterBody2D

@onready var animated_sprite_2d = $AnimatedSprite2D
@onready var gun_position = $GunPosition
@onready var guns = $GunPosition/SelectWeapon/Guns
@onready var player_bullet_marker = $GunPosition/SelectWeapon/Guns/PlayerBulletMarker
@onready var bullet_raycast = $GunPosition/SelectWeapon/Guns/PlayerBulletMarker/BulletRaycast




@export var speed : int = 300
@export var max_horizontal_speed = 300

@export var GRAVITY : int = 800
@export var jump_impulse : int = -300
@export var jump_horizontal_speed : int = 300
@export var max_jump_horizontal_speed : int = 300
@export var multi_jump_stacks : int = 2

var shooting : bool = false

enum State { Idle, Falling, Run, Jump, StandShoot, RunShoot, JumpShoot }
var current_state : State

func _physics_process(delta):
	character_facing()
	player_falling(delta)
	player_idle()
	player_stand_shoot()
	player_run()
	player_jump()
	player_run_shoot()
	player_jump_shoot()

	move_and_slide()
	
	player_animations()

	
	
func player_falling(delta):
	if !is_on_floor():
		velocity.y += GRAVITY * delta
		if shooting == true:
			current_state = State.JumpShoot
	
		
func player_idle():

	
	if is_on_floor() and velocity.x == 0:
		current_state = State.Idle
		
	if !is_on_floor() and shooting == true:
		current_state = State.JumpShoot
		


func player_run():
	var direction = input_movement()
	if direction:
		velocity.x += direction * speed
		velocity.x = clamp(velocity.x, -max_horizontal_speed, max_horizontal_speed)
	else:
		velocity.x = 0
	
	if direction != 0 and is_on_floor():
		current_state = State.Run

		
	if shooting == true and is_on_floor():
		current_state = State.RunShoot

func player_jump():
	
	if is_on_floor() and Input.is_action_just_pressed("jump"):
		multi_jump_stacks = 3
		velocity.y = jump_impulse
		current_state = State.Jump

		
	if !is_on_floor() and current_state == State.Jump:
		var direction = input_movement()
		velocity.x += direction * jump_horizontal_speed
		velocity.x = clamp(velocity.x, -max_jump_horizontal_speed, max_jump_horizontal_speed)
		
	if !is_on_floor() and multi_jump_stacks > 0 and Input.is_action_just_pressed("jump"):
		multi_jump_stacks -= 1
		velocity.y = jump_impulse
		print("Jumps remaining: ", multi_jump_stacks)

func player_stand_shoot():

	if shooting == true and input_movement() == 0 and is_on_floor():
		current_state = State.StandShoot
	
func player_run_shoot():
	var direction = input_movement()
	if direction:
		velocity.x += direction * speed
		velocity.x = clamp(velocity.x, -max_horizontal_speed, max_horizontal_speed)

	
	if direction != 0 and is_on_floor() and shooting == true:
		current_state = State.RunShoot
		
	if direction == 0 and is_on_floor() and shooting == true:
		current_state = State.StandShoot
	
	
func player_jump_shoot():
	
	if is_on_floor() and Input.is_action_just_pressed("jump") and shooting == true:
		velocity.y = jump_impulse
		current_state = State.JumpShoot
	elif !is_on_floor() and current_state == State.Jump and shooting == true:
		current_state = State.JumpShoot
	elif !is_on_floor() and current_state == State.JumpShoot and shooting == false:
		current_state = State.Jump
		
	if !is_on_floor() and current_state == State.JumpShoot:
		var direction = input_movement()
		velocity.x += direction * jump_horizontal_speed
		velocity.x = clamp(velocity.x, -max_jump_horizontal_speed, max_jump_horizontal_speed)
		

func input_movement():
	var direction : float = Input.get_axis("move_left", "move_right")
	return direction

func character_facing():
	var direction = input_movement()
	if direction > 0:
		animated_sprite_2d.flip_h = false
		gun_position.position.x = 20
		guns.scale.x = -0.255
		player_bullet_marker.position.x = -36
		bullet_raycast.target_position = Vector2(-1000, 0)
		

		
	if direction < 0:
		animated_sprite_2d.flip_h = true
		gun_position.position.x = -20
		guns.scale.x = 0.255 
		player_bullet_marker.position.x = -39
		bullet_raycast.target_position = Vector2(-1000, 0)
		

		


func player_animations():
	if current_state == State.Idle:
		animated_sprite_2d.play("idle")
	elif current_state == State.Run:
		animated_sprite_2d.play("run")
	elif current_state == State.Jump:
		animated_sprite_2d.play("jump")
	elif current_state == State.StandShoot:
		animated_sprite_2d.play("stand_shoot")
	elif current_state == State.RunShoot:
		animated_sprite_2d.play("run_shoot")
	elif current_state == State.JumpShoot:
		animated_sprite_2d.play("jump_shoot")
	elif current_state == State.Falling:
		animated_sprite_2d.play("falling")
		
func _on_base_weapon_pressing_shoot():
	shooting = true
	
func _on_select_weapon_releasing_shoot():
	shooting = false

Could you enclose all of your code inside a “preformatted text” block (can use triple back quotes).

This will make all of it formatted like code and easier to read.

How would one even debug this? You have to be giving an input to move into the wall after pressing jump to reproduce the bug.

I suspect the state machine and the transitions could be faulty. I’ll start with print messages to at least see what parts of the state machine are interacting.

I have included some of the print statements that were made when moving into a wall and spamming the jump button. I hope this can shed some light on the issue. Click here to view details:

State: Jump
On Floor!
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
On Floor!
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
On Floor!
State: Run
On Floor!
State: Run
On Floor!
State: Run
State: Run
On Floor!
State: Run
State: Jump
State: Jump
State: Jump
State: Jump
State: Jump
On Floor!
State: Jump
On Floor!
State: Run
State: Run
State: Run
On Floor!
State: Run
State: Run
State: Run
On Floor!
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
On Floor!
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
State: Run
On Floor!
State: Run
State: Run
State: Run
On Floor!
State: Run
On Floor!
State: Run

This may be easier to debug if you only run a single behavior function per frame, for the current state. EG, when current_state == State.Idle, then run a function that determines idle behavior, and none of the others. It may change the state, but that state won’t take over until the next frame.

Running every function every frame means you have 7x as much code to examine per frame when debugging. And makes it possible that two of them might run on the same frame if they do not correctly exclude the other’s behavior.

func _physics_process(delta):
    print(State.find_key(current_state), ' ', position, ' ', velocity); # which state to examine this frame?
    if current_state == State.Idle:
        idle_behavior()
    elif current_state == State.Run:
        run_behavior()
    elif ...

I have isolated the issue down to the fact that for some reason the walls are being detected as floors. I can’t understand why that happens.

Edit: fix discovered: (Thank you all for your help)
The characterbody2d’s collisionshape was a capsule, which caused the issues.
It has been modified to a rectangle (could also use box etc).

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.