Jumping on slope works only every other physics frame with kinematic body

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

I’m making a platformer and I am using a kinematic body. When it is on a slope, jumping only works sometimes. After further investigation I noticed that when on a slope it is_on_floor() returns true every other frame. Here is my whole player code, however I think only get_input() and _physics_process() functions are relevant.

extends KinematicBody2D

export (int) var speed = 130
export (int) var jump_speed = -300
export (float, 0, 1.0) var friction = 0.6
export (float, 0, 1.0) var acceleration = 0.9
export (int) var mass = 1
var outside_velocity = Vector2(0, 0)
var gravity
var velocity = Vector2.ZERO
var carrying = false
var carrying_object = null
var movement = true

var movable_bodies = []

signal on_screen_exited

func _init():
	add_to_group("player_group")

func _ready():
	gravity = get_parent().get_parent().get("gravity")
	if gravity == null:
		push_error("Parent of the player doesn't have gravity. Please put in a Level2D node. Setting gravity to 1300")
		gravity = 1300
func _on_VisibilityNotifier2D_screen_exited():
	queue_free()
	emit_signal("on_screen_exited")

func get_input():
	var dir = 0
	if Input.is_action_pressed("move_right"):
		dir += 1
	if Input.is_action_pressed("move_left"):
		dir -= 1
	if dir != 0:
		velocity.x = lerp(velocity.x, dir * speed, acceleration)
	else:
		velocity.x = lerp(velocity.x, 0, friction)	


func push():
	for i in get_slide_count():
		var colision = get_slide_collision(i)
		var collider = colision.collider
		if is_instance_valid(collider):
			if collider.is_in_group("physics_bodies"):
				if colision.normal.y == 0: #if the normal is horizontal
					velocity.x = (velocity.x * mass) / collider.mass
					collider.velocity.x = (velocity.x * mass) / collider.mass

func jump_and_push_up():
	if carrying:
		carrying_object.outside_velocity.y = jump_speed
		carrying_object.outside_velocity.x = velocity.x

func _physics_process(delta):
	if movement == true:
			velocity.y += gravity * delta
			get_input()
			if Input.is_action_just_pressed("move_down"):
				if not is_on_floor():
					velocity.y += -2 * jump_speed
	var bodies = push()
	if Input.is_action_just_pressed("move_jump"):
		if is_on_floor():
			velocity.y = jump_speed
			jump_and_push_up()
	velocity += outside_velocity
	velocity = move_and_slide(velocity, Vector2.UP, true)
#	velocity = move_and_slide(velocity, Vector2.UP, false, 4, PI/4, true)
	outside_velocity = Vector2(0, 0)


func _on_Area2D_body_entered(body):
	carrying = true
	carrying_object = body

func _on_Area2D_body_exited(body):
	carrying = false
	carrying_object = null
:bust_in_silhouette: Reply From: vnmk8

I think is because move_and_slide() function does not stick to the floor on slopes, so the is_on_floor() checks returns false when detects the body moves and separates from the floor, so jump can’t be triggered. you can use one or more raycasts to detect floor collision instead of is_on_floor() or use move_and_slide_with_snap()
so the is_on_floor() check is more accurate.

That makes fixes the every-other-frame-not-on-floor issue, however the character still doesn’t stay still on slopes. My guess is that it’s because it has to stand still. to combat this I added this to the code

			if get_floor_normal().x != 0:
			velocity.y = 0

That only slowed the moving down and didn’t stop it. I have no idea why

Update: I found a solution. Thank you for putting me on the right track. I removed the bit aforementioned bit of code. The solution is to increase the safe margin of the player to 0.1. I found it here: move_and_slide_with_snap() gliding down slopes and workarounds dont work since RC3-5 etc · Issue #47042 · godotengine/godot · GitHub

Moss | 2021-07-28 16:16

you have to set stop on slope to true in the move_and_slide() function. in your code you call the move_and_slide() function twice. first you set stop_on_slope to true and the second sets it to false that’s why your character is sliding, you can find all the possible arguments of that function in the documentation.

var stop_on_slope = true    
move_and_slide(linear_vel, FLOOR_NORMAL, stop_on_slope, 4, 0.9, true)

vnmk8 | 2021-07-28 16:34

The second one was commented out. My current code is this

func _physics_process(delta):
	if movement == true:
			velocity.y += gravity * delta
			if get_floor_normal().x != 0:
				velocity.y = 0
			get_input()
			if Input.is_action_just_pressed("move_down"):
				if not is_on_floor():
					velocity.y += -2 * jump_speed
	var bodies = push()
	if Input.is_action_just_pressed("move_jump"):
		if is_on_floor():
			snap = 0
			velocity.y = jump_speed
			jump_and_push_up()
	velocity += outside_velocity
	velocity = move_and_slide_with_snap(velocity, Vector2.DOWN * 10 * snap, Vector2.UP, true)
	outside_velocity = Vector2(0, 0)
	snap = 1

It works perfectly now. Thank you a lot

Moss | 2021-07-28 16:47

yeah you’re right, glad you made it work, you’re welcome.

vnmk8 | 2021-07-28 17:09