EDIT: Jump buffer only does a tiny bounce on slope (read replies)

Version 4.2

There is a alot to explain so i will try to do it quickly.
Essentially, in the game i am making, the player is able to run up slopes and walls like they are normal flat surfaces (kind of like Sonic the Hedgehog!). This is acheived mainly using these lines of code:

func _physics_process(delta):
	
	if is_on_floor():
		slopeangle = get_floor_normal().angle() + (PI/2)
		#angle of the floor rotated 90°
		
		slopefactor = get_floor_normal().x
		#affects acceleration based on slope steepness
	else:
		slopefactor = 0
		#slopes do not affect accelleration while in the air

	$Collision.rotation = rot
	
	$Sprite.rotation = lerp_angle($Sprite.rotation, rot, 0.25)

	if is_on_floor():

		up_direction = get_floor_normal()
		rot = slopeangle
	else:
		
		if not $Collision/Raycast.is_colliding() and grounded:
			grounded = false
			
			motion = get_real_velocity()
			
			rot = 0
			up_direction = Vector2(0, -1)

“$collision/Raycast” is just a small raycast pointing down towards the floor, placed at the bottom of the collisionshape2d.
There is also a peice of code which turns vertical momentum into horizontal momentum if the player lands on a slop of a certain angle:

	if is_on_floor():
		
		if not grounded:
			motion.x *= 0.8
			if abs(slopeangle) >= 0.25 and abs(motion.y) > abs(motion.x):
				motion.x += motion.y * slopefactor
				#adds vertical motion into horizontal motion when falling onto slope
			grounded = true

when the player jumps, they dont jump upwards, but instead in an up direction relative to their rotation:

		if abs(rot) > 1:
			position += Vector2(0,-(10)).rotated(rot)

in addition, theres a small line of code in the script that turns “Motion” into real velocity

	velocity = Vector2(motion.x, motion.y).rotated(rot)

With all of that being said, these things work fine on their own. However, in the specific circumstance that the player jumps at the moment they land on a slope, it launches them sideways at a speed which kind of breaks the movement system. I wish i could send a video to show what i mean, but im not allowed to send attachments :man_shrugging:

If you have any idea how i could fix this, i would love to know what it is. Thanks for your time, and take care.

I think i’ve resolved this problem through multiple changes that i cant explain but now im having a new issue. I switched the player’s collider shape into a capsule which smooths the movement when they are running on a slope. I also previously added a few lines of code which allow the player to jump after they land on the ground if they press jump 5 frames before. There was an issue with this though, whenever i lightly tapped jump before landing on the ground, i would instead get a small bounce. I have since resolved this, but the player still only does a small bounce if they jump while landing on a slope.

Where is your piece of code that responds to the jumping input and handles it?

Here’s everything jump related, although lots of it might be unrelated to the issue.

if Input.is_action_just_pressed("Jump"):
		jumpbuffered = true
		$JumpBufferTimer.start()
	
	#coyote timer
	if not grounded:
		if $CoyoteTimer.is_stopped and canjump:
			$CoyoteTimer.start()
	else: 
		$CoyoteTimer.stop()
	
	#jump
	if jumpbuffered and canjump:
		motion.y = -JUMP_VELOCITy
		jumping = true
		canjump = false
		
		if abs(rot) > 12:
			motion += Vector2(0,-(10)).rotated(rot)
		$JumpBufferTimer.stop()
		jumpbuffered = false
		
# allows you to jump agian after touching ground
	if motion.y >= 0 and grounded:
		jumping = false
		canjump = true

#allows for variable jumping
	if jumping and motion.y < -JUMP_VELOCITy / 2:
		if not Input.is_action_pressed("Jump"):
			motion.y = -JUMP_VELOCITy / 2
#jump hang time
	if jumping and motion.y > 0:
		$HangTimer.start()
		motion.y = 0
		jumping = false
		is_hanging = true
	

The “grounded” variable is set to true if Is_on_floor is true, and false if the raycast at the bottom of the player’s hitbox is not detecting ground (i cant send a picture of that unfortunately)

It’ll be hard to tell specifically the reason why it behaves incorrectly, but I guess it has to be tied with the timing of your “grounded” variable. Try debug printing all the relevant values - like grounded, canjump, jumping, etc. to see in what order specifically they fire. Maybe also think about implementing a state machine to explicitly handle each state separately instead of all at once

1 Like

Thanks for the suggestion. I’ll definitely try to implement a state machine so i can make some sense of my own code lol. I’ll reply back to this thread if i manage to fix it.

1 Like

Im back, and i finally implemented a state machine! (albiet, a very crude one.) I was having quite a few issues getting the jump to work, mainly because the “falling” state would occur right after the jump state and then the jump state wouldnt fire again. I fixed this by adding a boolean “Jumped” in conjunction with my “jumping” state, and after ironing out everything the bug is gone! Thank you for your help, wchc. I’ll put the state machine and jump code down here in case anybody wants to see what i did.

func _physics_process(delta: float) -> void:
	##set states
	var direction = Input.get_axis("Walk_Right", "Walk_Left") # get direction
	if is_on_floor():
		if direction:
			_set_state(States.RUNNING)
		else:
			_set_state(States.IDLE)
	var is_initiating_jump := is_on_floor() and Input.is_action_just_pressed("Jump")
	if is_initiating_jump:
		_set_state(States.JUMPING)
		jumped = true
	elif state == States.JUMPING or States.RISING and motion.y > 0 and not is_on_floor():
		_set_state(States.FALLING)
	elif jumped == true and motion.y < 0.0:
		_set_state(States.JUMPING)
	else:
		jumped = false
	if state == States.IDLE and Input.is_action_pressed("Shell"):
		_set_state(States.CROUCHING)

Section of script dedicated to jumping:

	if Input.is_action_just_pressed("Jump"):
		jumpbuffered = true
		$JumpBufferTimer.start()
	
	#coyote timer
	if state == States.FALLING:
		if $CoyoteTimer.is_stopped and canjump:
			$CoyoteTimer.start()
	else: 
		$CoyoteTimer.stop()
	
	#jump
	if jumpbuffered and is_on_floor():
		motion.y = -JUMP_VELOCITy #obvious what this means i hope
		canjump = false
		#send the player a bit to the side when jumping off slopes
		if abs(rot) > 12:
			motion += Vector2(0,-(10)).rotated(rot)
		$JumpBufferTimer.stop()
		jumpbuffered = false
		
# allows you to jump agian after touching ground
	if state == States.IDLE or States.RUNNING:
		canjump = true
#allows for variable jumping
	if state == States.JUMPING and motion.y < -JUMP_VELOCITy / 1.625:
		if not Input.is_action_pressed("Jump"):
			motion.y = -JUMP_VELOCITy / 1.625

_set_state function:

func _set_state(new_state: States) -> void:
	var previous_state := state
	state = new_state
	print(state) 
	if state == States.RISING or States.JUMPING:
		gravity = GRAVITY
	if state == States.FALLING:
		gravity = FALL_GRAVITY
	if previous_state == States.JUMPING and state == States.FALLING:
		gravity = FALL_GRAVITY * 0.95

Thanks again and have yourselves a good one