Search code examples
signalscollisiongodot

Godot odd behavior with on_body_entered and on_body_exited


I have a KinematicBody2D (player) and an Area2D (ladder). With the following code I get clean enter and exit events that happen as the player touches and leaves the Area2D object e.g. touch Area2D and a single "enter" prints. Move back and a single "exit" prints.

func _on_Ladder_body_entered(body):
    if body.name == "Hero":
        print("enter")

func _on_Ladder_body_exited(body):
    if body.name == "Hero":
        print("exit")

with this code things go wrong:

func _on_Ladder_body_entered(body):
    if body.name == "Hero":
        set_collision_layer_bit(0, false)
        print("enter")

func _on_Ladder_body_exited(body):
    if body.name == "Hero":
        set_collision_layer_bit(0, true)
        print("exit")

I get an entered event immediately followed by an exit event as soon as the player touches the Area2D. What am I doing wrong? Thanks! Godot version 3.4.2


Solution

  • Godot is doing what you are telling it to do.

    The call set_collision_layer_bit(0, false) would be disabling the collision between the KinematicBody2D and the Area2D which means they would no longer be colliding. And since they stop colliding, you get a "body_exited" signal.


    I want to turn off collisions so the player can pass the object and re-enable them when he passes and leaves the Area2D object

    The Area2D do not stop physics bodies (StaticBody2D, KinematicBody2D, RigidBody). There must be something else doing that. For example, if you added a StaticBody2D to the ladder, or made it with a TileMap where the tiles have collision, those could stop your KinematicBody2D.


    I think the simplest approach would be to put the Area2D in a different collision layer, so you can disable the collision that stops the KinematicBody2D without disabling the Area2D.

    Other options to disable collisions include:

    • Disabling a CollisionPolygon2D or CollisionShape2D by setting disabled to true. Note: you cannot to do this while Godot is resolving collision response. To avoid that, use set_deferred to change the value of disabled.
    • Adding a collision exception by calling add_collision_exception_with. Use remove_collision_exception_with to remove the exception. This is a more fine grained tool that should be used sparingly (if you are adding lots of exception, prefer using collision layers and masks).

    Addendum: An option for the TileMap is to have two version of the tile, one with the collision and one without it. And the swap them in runtime with set_cell. However, consider using a scene instead. In fact, you might want to set the cells in the designer and replace them with actual scenes in runtime. See How to use SCENES in a TILEMAP(And 9 MORE TRICKS) | Godot Engine. You may even do it only when the player gets nearby, see How to create large Sidescrolling Map in Godot?.