Having trouble with getting wall sliding and wall jumping to work in tandem of each other

Godot Version

4

Question

i have been trying get this working for 2 days now and still couldnt find a solution at all. TWT, all i need to be able to hold a horizontal input that causes me to wall slide, then if i press a a different horizontal input, it will over ride it into a wall jump.

holding left on a wall= wall slide, if i press right then it over rides into wall jump, hold right on a wall = wall slide and if i press left it overrides into wall jump, but thats not how this code is working right now, it just doesnt do the wall jump, only the wall slide

func handle_wall_jump():
	var horizontal_direction = Input.get_axis("ui_left","ui_right")
	var wall_normal = get_wall_normal()
	if is_on_wall():
		jump_count = 2
		parasol_girl.play("jumping")
		if Input.is_action_pressed("ui_left") and is_on_wall():
			velocity.y = 100
		elif Input.is_action_just_pressed("ui_right") and wall_normal == Vector2.RIGHT:
			velocity.x = wall_normal.x * speed
			velocity.y = JUMP_VELOCITY
		if Input.is_action_pressed("ui_right") and is_on_wall():
			velocity.y = 100
		elif Input.is_action_just_pressed("ui_left") and wall_normal == Vector2.RIGHT:
			velocity.x = wall_normal.x * speed
			velocity.y = JUMP_VELOCITY```
elif Input.is_action_just_pressed("ui_right") and wall_normal == Vector2.RIGHT:
elif Input.is_action_just_pressed("ui_left") and wall_normal == Vector2.RIGHT:

Seems like these two ifs are looking at the same wall normal, one of them should be Vector2.LEFT. I doubt this solves your issue though.

Also worth mentioning it’s a bad idea to do equality checks for floating points. You may get more accurate results using greater than /less than for the wall_normal.x like so

elif Input.is_action_just_pressed("ui_right") and wall_normal.x > 0:
elif Input.is_action_just_pressed("ui_left") and wall_normal.x < 0:

I could see the if statement after pressing right will always trigger and overrides the y velocity

elif Input.is_action_just_pressed("ui_right") and wall_normal == Vector2.RIGHT:
	velocity.x = wall_normal.x * speed
	velocity.y = JUMP_VELOCITY # SET JUMP
# still on the wall, and pressing ui_right, so this if triggers too
if Input.is_action_pressed("ui_right") and is_on_wall():
	velocity.y = 100 # SET NOT JUMPING

But this would also only apply to one side, maybe both issues combined is what you are experiencing.

the thing is, i copied the code from a yt video, i tried adding my own wall sliding mechanic. with out the wall sliding it works perfectly fine, but with it, all of it just breaks besides wall sliding. i did try doing the solutions you gave me but those didnt even. and even tried some of my own, still no luck. TwT ill probably just look at yt video instead unless you got another option

Could you share your code with the proposed fixes? I didn’t actually propose anything for the second issue, so I’m interested in how you aimed to solve it. With this extra information I’m pretty confident the problem is in the presented block of code.

i didnt to try and follow another tutorial but that code …doesnt even allow me to jump TwT, I TRIED WATCH 4 DIFFERENT WALL JUMPING VIDEOS, AND NON OF THEM WORKED, I CANT EVEN FIND ANY TO COPY AND PASTE FROM TWT

Yeah video tutorials are usually pretty bad in my experience. I try to stick to reading the Godot docs or blog posts, but I haven’t seen anything specific to wall jumping in Godot.

I’ll try my own hand at it tomorrow, maybe I’ll make it a Resources cross-post :upside_down_face:

Hi.

I’m going to try to help you understand your code. I think I can see why this isn’t working, but (and I’m sorry if I’m wrong) I don’t think you understand your code very well. That’s fine! We all start somewhere, but I think you’ll gain a lot more from working out what your code means than just copying code you’ve seen. It will enable you to write your own code from scratch to do things you can’t find tutorials for, and it’s very rewarding.

Firstly, this line:

var wall_normal = get_wall_normal()

Runs a method (some named code) called “get_wall_normal” and puts the value that it returns into a new variable (“var,” a place to store information) called “wall_normal” so that you can use it later. The method comes from another class called CharacterBody2D (I’m guessing you are extending from it at the top of your script) and it returns the direction the wall touching your character is facing. You can read about get_wall_normal here if you want to (searching this is really helpful in lots of cases): CharacterBody2D — Godot Engine (stable) documentation in English

Next the “if” and “elif” lines. They check if a condition is true, and if so run the code indented under them. If the condition isn’t true, they skip the indented code. “elif” means “else if”. Those lines only run if the “if” that came before didn’t.

The logic you want is something like:

  • if the character is on a wall:
    • get the wall’s normal (there’s no point trying to get it if you’re not on a wall!)
    • maybe play a sliding animation, reset your jump counter, etc. (but don’t worry about this for now, it’s probably better to use signals for this anyway)
    • and then if the wall is facing right (ie. it is to the character’s left)
      • if the character just pressed right, jump off the wall
      • else if the character is pressing left, slide on the wall
    • else (the wall must be facing left, so is on the character’s right)
      • if the character just pressed left, jump off the wall
      • else if the character is pressing right, slide on the wall

edit: I made a mistake too, but I just fixed it. I had the “slide on wall” check before the “jump off wall” check. This would have stopped jump working if still holding toward the wall when pressing away from it.

I’m not sure, but it sounds like you’re handling gravity elsewhere, so we don’t need to worry about falling in here.

If you read your code slowly and carefully and compare it, you’ll see the logic isn’t quite the same. Try to read it line by and following through what happens in a given situation, such as “what happens if I’m on a wall to my left and just pressed right?”. Getting into that habit will help you write code that usually doesn’t break in the first place, but will also help you figure out what’s wrong when it does.

Once you’ve figured out how the logic differs, you should be able to change what you have to do what you want. If you get stuck though, I’ll happily help some more.

If it still doesn’t work it could be a couple of other things.

  • get_wall_normal, according to the documentation I liked you to, only works if you’ve used move_and_slide() first. So maybe that’s missing? (but it is probably just in the _physics_process method, which is fine)
  • maybe when you let go of the direction toward the wall, or press the direction away from the wall, the character is moving away from the wall before this code can run. In that case the character will never be touching the wall when the direction is pressed and won’t jump. You might need to look into something called coyote time if that’s what is happening.

Good luck!

theres a resources here!!!

i changed the code agaaaiiin from another video, that still didnt work, im gonna go look at coyote time to see if that helps it until then
heres the current new wall jump code

if is_on_wall_only(): velocity.y = wall_slide_speed
   
   if Input.is_action_just_pressed("jump"):
   	if is_on_wall_only():
   		velocity.y = JUMP_VELOCITY
   		velocity.x = horizontal_direction * speed
   		wall_jump = true
   		wall_jump_timer.start()
   		
   if horizontal_direction && not wall_jump: velocity.x = horizontal_direction * speed
   elif not wall_jump: velocity.x = move_toward(velocity.x, 0, speed)	
   move_and_slide()


func _on_wall_jump_timer_timeout():
   wall_jump = false```

ok so i added coyote time, it works but now it broke it the crouching x.x if i uncrouch it just … crashes the game xd

Hey I’m glad the jump’s working at least, nice one. Looks like you did some pretty dramatic code changes to get there, did you not like wall jumping with just left/right and no jump input?

Lines 173 and 174 might be easier to debug if you wrap the if statements over the line (I actually didn’t know you could do single line if statements in Godot). That way you’ll know if the error is talking about the if’s condition or code block. But it looks like horizontal_direction might be a boolean rather than a float.

oh i ment the wall normal jumping of coyote works, but the wall jumping …is still broken with no chances. i changed nonthing about horizontal direction, its nonthing but a variable for the inputs left and right, i dont get hwo its crashing the game because im crouching, it was working perfectly fine x.x

extends CharacterBody2D
@onready var parasol_girl = $AnimatedSprite2D
@onready var collision = $CollissionShape2D
@onready var dash_shape = $dash_shape
@onready var crouch_shape = $crouch_shape
@onready var wall_jump_timer =  $wall_jump_timer
@onready var coyote = $coyote_timer
#const SPEED = 400.0 # Imahilus doesn't appear to be used
var speed = 500.0
# Jumping variables
const JUMP_VELOCITY = -480.0
const FAST_FALL_VELOCITY = 2000
const wall_slide_speed = 120
var jump_count = 0
var max_jumps = 2
var wall_jump_count = 0
var fast_fall = false
# Dashing variables
const DASH_SPEED = 2000
var dash_state = false
@export var dash_duration = 0.1
var time_spent_dashing = 0
@export var dash_cooldown = 1
var time_dash_spent_on_cooldown = 0
@onready var second_jump: bool = false
# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
var is_crouching = false
var vertical_direction = Input.get_axis("ui_down","ui_up")
var wall_jump = false
var was_on_floor = is_on_floor()

func _physics_process(delta):
	var horizontal_direction = Input.get_axis("ui_left","ui_right")
	if was_on_floor && !is_on_floor():
		coyote.start()
	if is_crouching:
		horizontal_direction = 0.0
	# Imahilus: I've commented this out, as the physics engine will apply the gravity to CharacterBody2D if 'motion_mode' is set to 'grounded' in the Inspector
	#Add the gravity.
	if not is_on_floor():
		velocity.y += gravity * delta
	
	# Time to handle the jump
	if is_on_floor():
		# We're on the floor, let's reset any jumps including the 'downwards jump'
		jump_count = 0
		fast_fall = false
	if Input.is_action_just_pressed("jump") and jump_count < max_jumps:
		coyote.is_stopped()
		jump_count += 1
		velocity.y += JUMP_VELOCITY
	if !fast_fall and Input.is_action_just_pressed("ui_down") and !is_on_floor():
		# If no 'downwards jump' has been triggered yet, we press the button to 'downward jump' AND we're not on the floor, do the drop!
		fast_fall = true
		jump_count = max_jumps # if we jump downwards.. no more jumping up!
		
	if fast_fall:
		# we're jumping downwards!! wheeee!
		velocity.y = FAST_FALL_VELOCITY
	
	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	if Input.is_action_just_pressed("Dash") and time_dash_spent_on_cooldown >= dash_cooldown: # If the button is pressed, and the last time Dash was used is longer than the cooldown time ago, we can dash!
		dash_state = true
		time_spent_dashing = 0 # reset the time we've spent Dashing
		time_dash_spent_on_cooldown = 0 # reset the cooldown on the Dash
		
	if dash_state:
		collision.disabled = true
		dash_shape.disabled= false
		velocity.y = 0
	else:
		collision.disabled = false
		dash_shape.disabled = true
	
	if dash_state:
		# We're dashing!!
		
		# Lets update the timers of the dash, so we don't permanently dash
		time_spent_dashing += delta
		if time_spent_dashing >= dash_duration:
			dash_state = false # We've spent more time dashing than was set, so we need to stop
		
		# Now lets actually do the dash part of it
		if parasol_girl.flip_h == false:
			# If parasol_girl is facing right; we dash to the right
			velocity.x = DASH_SPEED
		else:
			# If parasol_girl is facing left; we dash to the left
			velocity.x = -DASH_SPEED
	else:
		# We're not dashing... so we can look at normal input. We separate these 2 so that user controls can't influence the dash
		velocity.x = speed * horizontal_direction
	if velocity.x > 0:
		parasol_girl.flip_h = false
	elif velocity.x < 0:
		parasol_girl.flip_h = true
	
	# Last thing to do, if Dash is on cooldown, we need to count this time towards the cooldown, otherwise it won't come back
	# This can't be in with the Dash code because it has to happen regardless of whether we're dashing
	if time_dash_spent_on_cooldown < dash_cooldown:
		# We only need to do this until we've waited long enough (see line 67; until time_dash_spent_on_cooldown is more or equal to dash_cooldown)
		time_dash_spent_on_cooldown += delta
		
	else:
		
		if is_on_floor(): # Floor animation first
			if Input.is_action_pressed("ui_down"): # Crouch animation first 
				is_crouching = true
				parasol_girl.play("crouching")
				collision.disabled = true
				crouch_shape.disabled = false
				horizontal_direction = false
			elif Input.is_action_just_released("ui_down"): # Then uncrouch
				is_crouching =  false
				horizontal_direction = true
				collision.disabled = false
				crouch_shape.disabled = true
			elif horizontal_direction: # Then Running
				parasol_girl.play("running")
			else: # Then if nothing else is triggered IDLE
				parasol_girl.play("idle")
				
		else: # Air animation
		
		# Jump
					if jump_count >= 2: # if double jump
						parasol_girl.play("double_jump")
					else: # if simple jump
						parasol_girl.play("jumping")
					if velocity.y >1:
						parasol_girl.play("falling")
# Regardless of what we did in any if statement, if parasol_girl has any velocity, let the physics engine move her
	if is_on_wall_only(): velocity.y = wall_slide_speed
	
	if Input.is_action_just_pressed("jump"):
		if is_on_wall_only():
			velocity.y = JUMP_VELOCITY
			velocity.x = horizontal_direction * speed
			wall_jump = true
			wall_jump_timer.start()
			
	if horizontal_direction && not wall_jump: velocity.x = horizontal_direction * speed
	elif not wall_jump: velocity.x = move_toward(velocity.x, 0, speed)	
	move_and_slide()


func _on_wall_jump_timer_timeout():
	wall_jump = false

There are a couple of lines where you do:
horizontal_direction = false
or
horizontal_direction = true

That’s where horizontal_direction is becoming a boolean, and then later on causing the crash because you cannot multiply a boolean and a float.

Also, I noticed this section of code here:

# Imahilus: I've commented this out, as the physics engine will apply the gravity to CharacterBody2D if 'motion_mode' is set to 'grounded' in the Inspector
	#Add the gravity.
	if not is_on_floor():
		velocity.y += gravity * delta

The comment seems to imply the gravity calculation has been commented out, but it’s still there 2 lines below. I’m not sure if it is supposed to be or not, but if it is supposed to be removed it’s probably causing you other problems.

1 Like

oh that comment was suppose to be deleted when i deleted my old jump code, i guess i missed that