Search code examples
signalsgodotgdscript

How do I detect collisions between a KinematicBody2D and a RigidBody2D in Godot?


I am trying to make a simple game in Godot where a wall falls down and the player has to dodge it. The wall is a RigidBody2D with a sprite and CollisionShape2D attached. The player is a KinematicBody2D with a sprite and CollisionShape2D attached. The main scene is a Node2D with the player and wall as children of it. I have managed to get the player to detect collisions but when I emit a signal (which is connected to the wall script), the wall does nothing. I have already tried using the body_entered signal in the player script, but nothing happens. Does anybody know how to fix this?

My player script:

extends KinematicBody2D

var speed = 1300
var velocity = Vector2()
signal game_over

func get_input():
velocity = Vector2()
if Input.is_action_pressed('d'):
    velocity.x += 1
if Input.is_action_pressed('a'):
    velocity.x -= 1
velocity = velocity.normalized() * speed

func _physics_process(delta):
get_input()

move_and_slide(velocity, Vector2(0, 0), false, 4, 0.785, false)

for index in get_slide_count():
    var collision = get_slide_collision(index)
    if collision.collider.is_in_group("Level"):
        death()

func death():
    emit_signal("game_over")
    hide()

The wall script:

extends RigidBody2D

func _on_Player_game_over():
    print("player collided") #example to see if collision is detected

Solution

  • I can't tell why your code is not working. However, I would do it differently: Instead of emitting a signal, call a method on the collider:

        if collision.collider.is_in_group("Level"):
            collision.collider._on_Player_game_over()
            death()
    

    It is probably worth giving the method a better name.

    By the way, you can also check if the collider has the method. For example:

        if collision.collider.has_method("_on_Player_game_over"):
            collision.collider._on_Player_game_over()
            death()
    

    The rest of this answer is explaining how to handle the collision on the RigidBody2D with the "body_entered" signal.

    • First make sure the RigidBody2D has contact_monitor set to true.

    • Also set its contacts_reported to a value greater than 0. There can be multiple contacts with the same body, and you need to consider possible contacts with other bodies. A value around 8 is probably OK.

    • Have the "body_entered" signal of the RigidBody2D connected to itself.

    • in the method in the RigidBody2D that handles the "body_entered" signal you would get a body parameter:

      func _on_Wall_body_enter(body):
          print("Collision")
      

      By the way, I would like to encourage to take advantage of collision layer and masks. So you can narrow what collisions the RigidBody2D even checks for.

    • Then you can filter what body it is. You can, for example:

      • Use the name:

        func _on_Wall_body_enter(body):
            if body.name == "Player":
                print("Collision with player")
        
      • Use a group:

        func _on_Wall_body_enter(body):
            if body.is_in_group("Player"):
                print("Collision with player")
        
      • Use a class name (this requires to add a class_name to the player):

        func _on_Wall_body_enter(body):
            if body is Player:
                print("Collision with player")
        
      • Check for a method:

        func _on_Wall_body_enter(body):
            if body.has_method("death"):
                print("Collision with player")
        
    • And, of course, you can call a method on it notify. For example:

      func _on_Wall_body_enter(body):
          if body is Player:
              body.death()
      

      Or

      func _on_Wall_body_enter(body):
          if body.has_method("death"):
              body.death()