+1 vote

Hey all, I'm working on a platformer right now and I've come across a bug that's left me stumped. I'm using platforms with one way collision, so the player can jump through tiles and land on top of them. Most of the time this works as expected, but one in every 20 jumps onto a higher platform and you get bounced down right when the bottom of your character meets the top of the platform. It's difficult to describe, so I've included a video demonstrating the bug, as well as showing some normally behaving jumps: https://youtu.be/BWl8IS3hHik

I've noticed the bug only seems to happen when you are moving either left or right while jumping. Another thing I've noticed is that the player seems to move up about a pixel whenever you walk left or right, and drop back down a pixel when you stop moving (perhaps this is related to the bug?)

I've included the player's physics_process code below. Does anyone know what might be causing this? If any other information can help, please let me know.

func _physics_process(delta):

if alive:
    var walk = 0

    if can_move: #need to test if 
        if Input.is_action_pressed("ui_left"):  
            walk = -WALK_FORCE
            if (!facing_left):
                facing_left = true 
                $AnimatedSprite.flip_h = true
                $Sword.position.x = -SWORD_DISTANCE
        elif Input.is_action_pressed("ui_right"):
            walk = WALK_FORCE
            if (facing_left):
                facing_left = false
                $AnimatedSprite.flip_h = false
                $Sword.position.x = SWORD_DISTANCE
        if can_jump and Input.is_action_just_pressed("ui_jump"):
            velocity.y = -JUMP_FORCE
            can_jump = false
            play_jump_sound()
            jumping = true
            my_gravity = gravity * JUMP_GRAVITY_MODIFIER
        elif jumping:
            if check_grounded() or !Input.is_action_pressed("ui_jump"):
                jumping = false
                my_gravity = gravity

    if (walk == 0): #decelerate the player if they're not walking
        velocity.x = move_toward(velocity.x, 0, STOP_FORCE * delta)
    else:
        velocity.x += walk * delta #accelerate the player in their desired direction


    #bring the player towards max speed
    if velocity.x > MAX_WALK_SPEED:
        velocity.x = move_toward(velocity.x, MAX_WALK_SPEED, DECELERATE_FORCE * delta)
    elif velocity.x < -MAX_WALK_SPEED:
        velocity.x = move_toward(velocity.x, -MAX_WALK_SPEED, DECELERATE_FORCE* delta) 

    #velocity.x = (velocity.x, -WALK_MAX_SPEED, WALK_MAX_SPEED) #prevent player from exceeding max_walk_speed

    apply_gravity(delta)


    velocity = move_and_slide(velocity, Vector2(0, -1))

    was_grounded = is_grounded
    is_grounded = check_grounded()
    if is_grounded != was_grounded:
        emit_signal("grounded_updated", is_grounded)

    if !was_grounded and is_grounded: #just landed, so play landing sound
        playing_walking_sound = false
        play_step_sound(-2)
    elif is_on_floor() and walk != 0:
        #play the initial step sound
        if !is_walking:
            is_walking = true
            play_step_sound()
            $StepTimer.start()
    elif is_walking:
        is_walking = false


    if (is_on_floor() and !can_jump):
        can_jump = true
    elif (!is_on_floor() and $JumpTimer.is_stopped()) and can_jump:
        $JumpTimer.start()
in Projects by (13 points)

1 Answer

0 votes

Looks like it is a bug of the physics engine: sometimes move_and_slide falsely detects floor when jumping on a one-way collision platform from below with non-zero horizontal speed.
In this case it returns velocity with y == 0, which is expected behavior in case of correct floor detection, but in our case it cuts the jump before a body is above the platform.
Unfortunately I wasn't able to fix the root cause of the problem, however I found a workaround that at least fixes the symptoms:

var alteredVelocity = move_and_slide(velocity, Vector2(0, -1), true)
if (alteredVelocity.y == 0) && (velocity.y < 0) && is_on_floor():
    alteredVelocity.y = velocity.y
velocity = alteredVelocity

As you can see, I check for a combination of 3 parameters, which most likely means that the body was moving up (jumping), and then is_on_floor() detection happened and alteredVelocity.y returned by move_and_slide has become 0.
In such case I set alteredVelocity.y back to the original velocity.y which allows the current jump to go on.

I'll appreciate if someone suggests more elegant solution, which would eliminate the false floor detection in the first place.

by (32 points)
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 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 webmaster@godotengine.org with your username.

Categories