• Godot Help2D
  • Collision normals not detected when collider hits the body in multiple points

Hi,

As Godot KinematicBodies are good for snapping to slopes but not for stairs, I extended KinematicBody2D for that purpose.

In short, the idea at a glance is:

  1. Test collision from current movement before moving on next physics step.
  2. If the body collides with the floor, collision normal is horizontal and under a y threshold defined in the extended kinematic body, then it is considered that the body collided with a step.
  3. The body "snaps" to the step, so that it reaches the next one.
func _update_movement_if_stepping_stairs() -> void:
    tested_next_collision = move_and_collide(movement, true, true, true) 
    if tested_next_collision == null:
        return
    
   update()
    
    if is_equal_approx(movement.x, 0.0):
        return

    if abs(tested_next_collision.normal.x) < 0.9:
        return
        
    if abs(tested_next_collision.normal.y) > 0.05:
        return

    var global_position_threshold_step: Vector2 = global_position + Vector2(0.0, threshold_to_snap_on_steps_y)

    if !tested_next_collision.position.y > global_position_threshold_step.y:
        return

    global_position.y -= threshold_to_snap_on_steps_y + get_safe_margin() 

As you can see in the attached gif, the next collision is rendered, alongside its normal. Problem is, the normal is not shown as horizontal as expected. Only in frame 152 from gif, when the body is jumping and not touching the floor, the collision normal is shown as horizontal, as expected.

At this point I feel a little lost here, not sure if colliding with the vertical part of the step not bringing an horizontal normal is a bug, if collision with floor (vertical normal) is overriding the one I'm expecting, or if I can follow a different approach to get the result I'm willing.

TIA!

I think I found the issue. I was calling the snapping method before move_and_slide:

func _update_movement() -> void:
    _update_movement_if_stepping_stairs()
    movement = move_and_slide(movement, Vector2(0, -1))

This was causing that the tested "next" collision was always the current collision. Swapping both method lines made the trick. Well, sort of, still some things to polish 😃