Character won't change directions for some reason

Godot Version 4.5.1

Question

Ok so I’m a newbie and I’ve been trying out a wall jump move (which also isn’t working quite right but that’s beside the point), and I noticed that when I hit the ground and I’m still holding in the direction of the wall, my character keeps moving in the opposite direction, the direction they were moving when they jumped off of the wall. Anyone know why this is?

And sorry that my code is such a mess! If you’ve got other ways to help clean this up, please do share!

extends CharacterBody2D

@export var weight = 5
@export var JUMP = -3000
@export var speed = 500
@export var maxspeed = 1500
@export var boostmax = 100000
@export var decel = 50
var BOOST
var SLIDE
var force
var ismove


@onready var coyote_timer: Timer = $CoyoteTimer
var coyote_time_on
@onready var sprite: Sprite2D = $Sprite2D
@onready var collbox: CollisionShape2D = $CollisionShape2D
@onready var animsprite: AnimatedSprite2D = $AnimatedSprite2D

enum States {idle, jump, fall, walk, boost, dive, decelerate, landing, slide, crouch, hurt}
var State = States

func _ready() -> void:
	State = States.idle
	BOOST = false
	SLIDE = false
	ismove = false
	coyote_time_on = false
	force = 5000
	
func _unhandled_input(InputEvent) -> void:
	#jump
	if Input.is_action_just_pressed("Jump") and (is_on_floor() or not coyote_timer.is_stopped()) and SLIDE == false:
		#change state to jump
		State = States.jump
		coyote_timer.start()
		coyote_time_on = true
		#jump increases with velocity.x
		if velocity.x > 0:
			velocity.y += JUMP - velocity.x/4
		elif velocity.x < 0:
			velocity.y += JUMP + velocity.x/4
		else:
			velocity.y += JUMP
	#variable jump height
	if Input.is_action_just_released("Jump") and velocity.y < 0:
		velocity.y = JUMP/4.0
		
		
	if Input.is_action_pressed("Boost") and force > 0:
		BOOST = true
	if Input.is_action_just_released("Boost") or force <= 0:
		BOOST = false
	
	##noticed from wall jump: will keep going in the direction of the last velocity.x even if the opposite direction is being held
	#move right
	if Input.is_action_pressed("Right") and BOOST == false and velocity.x <= maxspeed and SLIDE == false:
		velocity.x += speed
		State = States.walk
		ismove = true
	#boost right
	elif Input.is_action_pressed("Right") and BOOST == true and velocity.x <= boostmax and SLIDE == false and is_on_floor():
		velocity.x += speed
		State = States.boost
		ismove = true
	#move left
	if Input.is_action_pressed("Left") and BOOST == false and velocity.x >= -maxspeed and SLIDE == false:
		velocity.x -= speed
		State = States.walk
		ismove = true
	#boost left
	elif Input.is_action_pressed("Left") and BOOST == true and velocity.x >= -boostmax and SLIDE == false and is_on_floor():
		velocity.x -= speed
		State = States.boost
		ismove = true
		
	if Input.is_action_just_released("Right") or Input.is_action_just_released("Left"):
		ismove = false
		
	
	#dive
	if Input.is_action_just_pressed("Down") and not is_on_floor():
		velocity.y += -JUMP * weight
		State = States.dive
		
	#slide
	if Input.is_action_pressed("Down") and is_on_floor() and velocity.x != 0:
		State = States.slide
	#crouch
	elif Input.is_action_pressed("Down") and is_on_floor() and velocity.x == 0:
		State = States.crouch
		

func _physics_process(delta: float) -> void:
 #Add the gravity.
	velocity += get_gravity() * delta * weight
	#flip sprite
	if velocity.x < 0:
		sprite.flip_h = true
		#animsprite.flip_h = true
	if velocity.x > 0:
		sprite.flip_h = false
		#animsprite.flip_h = false
		
		
	#idle state
	if State == States.idle:
		print("idle")
		SLIDE = false
		if not is_on_floor() and velocity.y > 0:
			State = States.fall
			
	#jump state
	if State == States.jump:
		print("jump")
		if velocity.y >= 0:
			State = States.fall
		
		#decelerate from boost
		if BOOST == false:
			if velocity.x > maxspeed:
				velocity.x -= decel
			if velocity.x < -maxspeed:
				velocity.x += decel
			
	#fall state
	if State == States.fall:
		print("fall")
		if is_on_floor():
			State = States.landing
			
		#decelerate from boost
		if BOOST == false:
			if velocity.x > maxspeed:
				velocity.x -= decel
			if velocity.x < -maxspeed:
				velocity.x += decel
			
	#landing state
	if State == States.landing:
		print("landed")
		if velocity.x == 0:
			State = States.idle
		if velocity.x != 0 and BOOST == false:
			State = States.walk
		if velocity.x != 0 and BOOST == true:
			State = States.boost
		if velocity.x == 0 and Input.is_action_pressed("Down"):
			State = States.crouch
		if velocity.x != 0 and Input.is_action_pressed("Down"):
			State = States.slide
			
	#walk state
	##noticed from wall jump: will keep going in the direction of the last velocity.x even if the opposite direction is being held
	if State == States.walk:
		print("walking")
		SLIDE = false
		if not is_on_floor() and velocity.y >= 0:
			State = States.fall
		elif not is_on_floor() and velocity.y < 0:
			State = States.jump
		
		#decelerate from boost
		if velocity.x > maxspeed:
			velocity.x -= decel
		if velocity.x < -maxspeed:
			velocity.x += decel
		#doesn't enter decelerate from walk if boost is released after right or left
		if ismove == false:
			State = States.decelerate
			
	#boost state
	if State == States.boost:
		print("boosting")
		SLIDE = false
		force -= 10
		if force < 0:
			force = 0
		if not is_on_floor() and velocity.y >= 0:
			State = States.fall
		elif not is_on_floor() and velocity.y < 0:
			State = States.jump
		if is_on_floor() and BOOST == false:
			State = States.walk
			
	#dive state
	if State == States.dive:
		print("dive")
		if is_on_floor():
			State = States.landing
	
	#decelerate state
	if State == States.decelerate:
		print("decelerating")
		if velocity.x > 0:
			velocity.x -= decel
		elif velocity.x < 0:
			velocity.x += decel
		elif velocity.x == 0:
			State = States.idle

	#slide state
	if State == States.slide:
		print("slide")
		##collbox change
		SLIDE = true
		if velocity.x > 0:
			velocity.x -= decel
		elif velocity.x < 0:
			velocity.x += decel
		elif velocity.x == 0:
			State = States.idle
		
		if velocity.x == 0:
			State = States.crouch
		if Input.is_action_just_released("Down") and velocity.x != 0:
			State = States.walk
			
	#crouch state
	if State == States.crouch:
		print("crouch")
		SLIDE = true
		if Input.is_action_just_released("Down") and velocity.x == 0:
			State = States.idle
			
		
	#hurt state
	#if State == States.hurt:
		#print("hurt")
		#force -= 50
		#if force > 0:
			#if is_on_floor():
				#velocity.x = 0
				#State = States.crouch
		#else:
			##die
			
	
	if is_on_floor() == true:
		if coyote_time_on == true:
			coyote_time_on = false
			coyote_timer.stop()
			
	else:
		if coyote_time_on == false:
			coyote_timer.start()
			coyote_time_on = true
	
	print(velocity.x)
	print(force)
	move_and_slide()
	
	##wall jumps still trigger when on the ground. They shouldn't
	##can't wall jump while boosting?
	#wall jump on right wall
	if Input.is_action_pressed("Right") and Input.is_action_pressed("Jump") and is_on_wall() and not is_on_floor():
		velocity.y += JUMP
		velocity.x += JUMP
		State = States.jump
	
	#wall jump on left wall
	if Input.is_action_pressed("Left") and Input.is_action_pressed("Jump") and is_on_wall() and not is_on_floor():
		velocity.y += JUMP
		velocity.x += JUMP
		State = States.jump

I’m pretty new here too but I’ve got an idea. Do us a favor and print the value of velocity.x while the game is playing, tell us what happens. If it’s not going below 0 then maybe that’s why. Maybe a different check something like

if (old_position != null) && (self.position.x > old_position):
    sprite.flip_h = true
elif (old_position != null) && (self.position.x < old_position):
    sprite.flip_h = false
var old_position = self.position.x

idk, just spitballing, here.

EDIT: Also print the value of `sprite.flip_h` and see if it’s changing.

One big, obvious problem with your code is the lack of consistency in your identifiers. You have variables with the following different styles:

  • All upper case, as in BOOST.
  • All lower case with underscores, as in coyote_time_on.
  • All lower case without underscores, as in maxspeed.
  • First letter capitalized, as in State.

This may seem irrelevant - after all, you have autocomplete to find the right identifier - but it actually makes your code hard to read because how an identifier is styled tells the reader about what kind of thing the identifier refers to, and here it’s telling the reader lies.

The conventions in GDScript are:

  • PascalCase for enum types and classes, or types in general (but not for built-in primitive types like float and int).
  • UPPER_SNAKE_CASE for constants (including enum values). Readers use this to see if something is a constant or variable.
  • lower_snake_case for everything else: functions, variables, and signals, and also built-in primitive types like float and int.