Help a noob figure out wall jumping

Godot Version

Godot 4.3

Question

Im very new and trying to make a dark fantasy platformer. Right now im working on getting movement implemented before I work on the harder stuff like combat and what not and I want to implement a wall jump. Specifically one that doesn’t incentivize or even allow the player to just hold one direction key and spam jump to climb the wall. To ascend the player should have to jump from wall to wall. The following is my entire player script. I KINDA have wall jumping implemented to a degree but no matter what I try the player doesnt kick off the wall. Am I doing something wrong or is my code just too simple?

extends CharacterBody2D

const SPEED = 180.0
const JUMP_VELOCITY = -250.0
const walljumpvelocity = -220
const walljumpHspeed = -120

var is_wall_sliding = false
var friction = 120

@onready var animated_sprite_2d: AnimatedSprite2D = $AnimatedSprite2D

func _physics_process(delta: float) → void:
wall_slide()
wall_jump()

# gravity.
if not is_on_floor():
	velocity += get_gravity() * delta

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

func wall_jump():
if is_on_wall_only() and Input.is_action_just_pressed(“Jump”):
velocity.y = walljumpvelocity
velocity.x = walljumpHspeed

# Wallsliding

func wall_slide():
if (is_on_wall_only()) && velocity.y > 0:
is_wall_sliding = true
velocity.y = friction
else:
is_wall_sliding = false

# Get the input direction and handle the movement/deceleration.
var direction := Input.get_axis("Move_left", "Move_Right")
if direction:
	velocity.x = direction * SPEED
else:
	velocity.x = move_toward(velocity.x, 0, SPEED)

# Sprite Flipping
if direction > 0:
	animated_sprite_2d.flip_h = false
elif direction < 0:
	animated_sprite_2d.flip_h = true

# Animation ifs
if is_on_floor():
	if direction == 0:
		animated_sprite_2d.play("Idle")
	else:
		animated_sprite_2d.play("Run")
else:
	animated_sprite_2d.play("Jump")
if (is_wall_sliding):
	animated_sprite_2d.play("Wall Slide")

move_and_slide()

The basic movement template sadly does not support acceleration or forces well. If there is a player input (direction) then your .x speed is directly set to the max speed, when there isn’t a player input it is using move_toward, but as a such a high rate that it’s similar to immediately setting it to zero speed.

You will have to change most of your velocity sets to use move_toward or addition instead of instant assignments.

For example, converting the above script to use move_toward properly. This assumes you would make a ACCELERATION and DECELERATION variable about eight times your speed.

if direction:
	velocity.x = move_toward(velocity.x, direction*SPEED, ACCELERATION*delta)
else:
	velocity.x = move_toward(velocity.x, 0, DECELERATION*delta)

Some games limit the player’s acceleration in the air, this can help prevent wall climbing while keeping the movement fast.


In this section the wall jump will always result in negative 120 on the x axis, which is only ever to the left. If you want to jump to the right you will need to detect which direction is away from the wall, this can be done with normals.

var normal: Vector2 = get_last_slide_collision().get_normal()
velocity += normal * walljumpHspeed

Make sure to paste scripts with proper formatting

I appreciate the reply. I actually ended up fixing the issue before the moderator approved my post.

my main issue was simply that walljumpHspeed simply wasnt high enough with the basic physics. From there I tweaked the physics and added acceleration and deceleration to add a sense of momentum and now the wall jumping works relatively well (so long as you dont take into account the janky animations) but the actual movement FUNCTIONS and thats the important part. Right now Im trying to figure out ledge climbing.

Ill post my current player script including the fix just in case anyone else finds this thread and is having this problem.

extends CharacterBody2D 

const jump_force = -350.0
const WALL_JUMP_VELOCITY = -300.0
const WALL_JUMP_HSPEED = 350.0
const GRAVITY = 800.0
const WALL_SLIDE_SPEED = 50.0
const LEDGE_CLIMB_OFFSET = Vector2(0, -40)

@export var Walk_Speed = 150.0   
var is_wall_sliding = false
var wall_jump_cooldown = 0.2
var wall_jump_timer = 0.0
var isGrabbing = false
var isFalling = false
var current_position = "position"
var climb_position = global_position + Vector2(30, -30)
var Run_Speed = 220.0
@export_range(0, 1) var deceleration = 0.1
@export_range(0, 1) var acceleration = 0.1
@export_range(0, 1) var decelerate_on_jump_release = 0.5


@onready var animated_sprite_2d: AnimatedSprite2D = $AnimatedSprite2D
@onready var grab_hand_rc: RayCast2D = $Raycasts/Walljump/GrabHandRC
@onready var grab_check_rc: RayCast2D = $Raycasts/Walljump/GrabCheckRC


func _physics_process(delta: float) -> void:
	_check_ledge_grab()
	ledge_climb() 

	# Add gravity
	if not is_on_floor():
		velocity.y += GRAVITY * delta
		
	wall_slide()
	wall_jump()

	# Add gravity if not on the floor or wall sliding
	if not is_on_floor() and not is_wall_sliding:
		velocity.y += GRAVITY * delta

	# Handle jump
	if Input.is_action_just_pressed("Jump") and (is_on_floor() || isGrabbing):
		isGrabbing = false
		velocity.y = jump_force
		isFalling = true

	if Input.is_action_just_released("Jump") and velocity.y < 0:
		velocity.y *= decelerate_on_jump_release

	if isGrabbing: return
	
	var speed
	if Input.is_action_pressed("Run"):
		speed = Run_Speed
	else:
		speed = Walk_Speed
		
	# Handle movement
	var direction := Input.get_axis("Move_left", "Move_Right")
	if direction != 0:
		velocity.x = move_toward(velocity.x, direction * speed, speed * acceleration)
	else:
		velocity.x = move_toward(velocity.x, 0, speed * deceleration)

	# Sprite Flipping
	if direction > 0:
		animated_sprite_2d.flip_h = false
	elif direction < 0:
		animated_sprite_2d.flip_h = true

	# Running, Jumping, Idle Animation
	if is_on_floor():
		if direction == 0:
			animated_sprite_2d.play("Idle")
		else:
			animated_sprite_2d.play("Run")
	
	if is_on_floor() and Input.is_action_pressed("Jump"):
		animated_sprite_2d.play("Jump")
	if is_wall_sliding and is_on_wall():
		animated_sprite_2d.play("Wall Slide")
	if is_on_wall() and Input.is_action_just_pressed("Jump") and isGrabbing == false:
		animated_sprite_2d.play("Wall_Jump")
		animated_sprite_2d.flip_h
	

	# Apply movement
	move_and_slide()

func _check_ledge_grab():
	var isFalling = velocity.y >= 0
	var checkHand = not grab_hand_rc.is_colliding()
	var checkGrabHeight = grab_check_rc.is_colliding()
	
	var canGrab = isFalling && checkHand && checkGrabHeight && not isGrabbing && is_on_wall_only()
	
	if canGrab:
		isGrabbing = true
		animated_sprite_2d.play("Ledge Grab")
		
func ledge_climb(): #Current project. Minimal progress made in last 9 hours.
	if isGrabbing and Input.is_action_just_pressed("Climb"):
		$AnimationPlayer.play("Ledge Climb")
		
func wall_jump():
	if is_on_wall_only() and Input.is_action_just_pressed("Jump"):
		var wall_direction = get_wall_direction()
		velocity.y = WALL_JUMP_VELOCITY
		velocity.x = wall_direction * WALL_JUMP_HSPEED
		
	
		# Start cooldown timer
		wall_jump_timer = wall_jump_cooldown
		is_wall_sliding = false
	  
func wall_slide():
	if is_on_wall_only() and velocity.y > 0:
		is_wall_sliding = true
		velocity.y = WALL_SLIDE_SPEED
	else:
		is_wall_sliding = false

func get_wall_direction() -> int:
	# Determine if the wall is on the left (-1) or right (1)
	if is_on_wall():
		var test_left = test_move(global_transform, Vector2(-1, 0))
		var test_right = test_move(global_transform, Vector2(1, 0))
		if test_left:
			return 1 # Wall is on the left, jump to the right
		elif test_right:
			return -1 # Wall is on the right, jump to the left
	return 0