0 votes

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 isonfloor() returns true every other frame. Here is my whole player code, however I think only getinput() and _physicsprocess() 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
Godot version v3.3.2.stable.fedora
in Engine by (15 points)

1 Answer

+1 vote
Best answer

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.

by (454 points)
selected by

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: https://github.com/godotengine/godot/issues/47042

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 stoponslope 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)

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

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

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to [email protected] with your username.