Moving and jumping on slopes with variable jump height, friction and acceleration

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Ypso

Hi,
I used walking speed acceleration, friction and variable jump height (the longer the jump button is pressed, the higher the jump), which worked fine as long it is on even ground, but causes problems on slopes:

  • the character could barely move uphills cause of the small starting speed when using acceleration. I used something like
    motion.x = min(motion.x+ACCELERATION, MAXSPEEDWALK)
  • due to the friction the character was sliding way too much after stoping movement. I used “motion.x = lerp(motion.x, 0, 0.2)” for it. Adjusting the weight works, but then you can not notice it on even ground (now I use a fixed value instead).
  • jumping works not as intended, when running there is barely a jump at all. Works fine on even ground.

So is there a way to tell the player that he/she is on a slope, like with using “is_on_floor()”?
I have removed friction and acceleration for movement on ground, because I could not get it work on slopes (code I used is shown above)
But the problem with jumping still exists, jumping on slopes while running does barely work, there is only a mini-jump.

I hope this are not too much question and there is a solution to fix it, thanks so much!

Part of the code, I can not show all because I get an error with “Maximum length is 8000 characters”:

const UP = Vector2(0, -1)
const GRAVITY = 20
const ACCEL = 15
const ACCELFLY = 15
const MAXSPEEDWALK = 100
const MAXSPEEDRUN = 200
const JUMP = 50
const HIGHJUMP = 350
var motion = Vector2()
var wait = 0
func _physics_process(delta):


# RUN STATE ---------------------------------------------------------------
elif states == run:
	
	if Input.is_action_pressed("ui_right") and not Input.is_action_pressed("ui_left"):
		if Input.is_action_pressed("ui_cancel"):
			
			motion.x = MAXSPEEDRUN
			$Sprite.flip_h = true
			$Sprite.play("run")
			if Input.is_action_pressed("ui_select"):
				states = runjump
				
		else:
			states = endrun
			
	elif Input.is_action_pressed("ui_left") and not Input.is_action_pressed("ui_right"):
		if Input.is_action_pressed("ui_cancel"):
			
			motion.x = -MAXSPEEDRUN
			$Sprite.flip_h = false
			$Sprite.play("run")
			if Input.is_action_pressed("ui_select"):
				states = runjump
				
		else: 
			states = endrun
		
	if Input.is_action_just_released("ui_left") or Input.is_action_just_released("ui_right"):
		states = endrun
	
# JUMP ----------------------------------------------------------------------
elif states == jump:
	if Input.is_action_pressed('ui_select'):
		motion.y = max (motion.y-JUMP, -HIGHJUMP)
		if motion.y == -HIGHJUMP:
			states = fall
	else: states = fall
	
	if is_on_ceiling():
		states = fall
	
	if motion.y < 0:
		$Sprite.play("jump")
		
	if motion.y > 0:
		states = fall
		
	if Input.is_action_pressed("ui_right") and not Input.is_action_pressed("ui_left"):
		$Sprite.flip_h = true
		if is_on_floor():
			motion.x = MAXSPEEDWALK
		else:
			motion.x = min(motion.x+ACCELFLY, MAXSPEEDWALK)
	
	elif Input.is_action_pressed("ui_left") and not Input.is_action_pressed("ui_right"):
		$Sprite.flip_h = false
		if is_on_floor():
			motion.x = -MAXSPEEDWALK
		else:
			motion.x = max(motion.x-ACCELFLY, -MAXSPEEDWALK) 
	else:
		motion.x = lerp(motion.x, 0, 0.05)
# RUN JUMP ----------------------------------------------------------------------
elif states == runjump:
	if Input.is_action_pressed('ui_select'):
		motion.y = max (motion.y-JUMP, -HIGHJUMP)
		if motion.y == -HIGHJUMP:
			states = runfall
	else: states = runfall
	
	if is_on_ceiling():
		states = fall
	
	if motion.y < 0:
		$Sprite.play("startrunjump")
		
	if motion.y > 0:
		states = runfall
		
	if Input.is_action_pressed("ui_right") and not Input.is_action_pressed("ui_left"):
		$Sprite.flip_h = true
		if is_on_floor():
			motion.x = MAXSPEEDRUN
		else:
			motion.x = min(motion.x+ACCELFLY, MAXSPEEDRUN)
	
	elif Input.is_action_pressed("ui_left") and not Input.is_action_pressed("ui_right"):
		$Sprite.flip_h = false
		if is_on_floor():
			motion.x = -MAXSPEEDRUN
		else:
			motion.x = max(motion.x-ACCELFLY, -MAXSPEEDRUN)
	else:
		motion.x = lerp(motion.x, 0, 0.05)


# FALL ------------------------------------------------------------
elif states == fall:
	if motion.y < 0:
		$Sprite.play("jump")
		
	if motion.y > 0:
		$Sprite.play("fall")
		
	if is_on_floor():
		states = hitground
		
	if Input.is_action_pressed("ui_right") and not Input.is_action_pressed("ui_left"):
		motion.x = min(motion.x+ACCELFLY, MAXSPEEDWALK)
		$Sprite.flip_h = true
	
	elif Input.is_action_pressed("ui_left") and not Input.is_action_pressed("ui_right"):
		motion.x = max(motion.x-ACCELFLY, -MAXSPEEDWALK)
		$Sprite.flip_h = false
	else:
		motion.x = lerp(motion.x, 0, 0.05)
		


motion.y += GRAVITY
motion = move_and_slide(motion, UP, 5, 4, deg2rad(50))
:bust_in_silhouette: Reply From: Levi Lindsey

This function should help to determine whether the player is on a “slope” vs a “floor”.

const UP = Vector2(0, -1)
const DEFAULT_MAX_FLOOR_ANGLE = deg2rad(5)

# This function assumes that you are already using move_and_slide, and that a "slope" is a subtype of a "floor", so if is_on_slope() is true, then is_on_floor() must also be true.
# If there are simultaneous collisions with both a "floor" and a "slope", then this returns false.
func is_on_slope(max_floor_angle = DEFAULT_MAX_FLOOR_ANGLE):
    if is_on_floor():
        for i in range(get_slide_count()):
            var collision = get_slide_collision(i)
            # Is this a "floor" collision?
            if collision.normal.angle_to(UP) <= max_floor_angle:
                return false
        # We didn't find a "floor" collision, but is_on_floor() is true, so there must be a "slope" collision.
        return true
    # is_on_floor is false, so there cannot be a "slope" collision.
    return false