Procedural collision detection on a tilemap (Doors)

Godot Version

4.2.1-stable

Question

I’m making a 2D top-down rogue-lite game where the player will navigate a procedural dungeon with rooms of different size interconnected with doors.

I’m at the point where I’m implementing a proper interaction system whereas before, I used simple physic collision on the tilemap which was working fine, but far from optimal.

  • 1st: Player is on Layer 2; as is its Area2D. The latter has mask 3 & 4 active.
  • 2nd: The procedurally generated Area2D comes from a InteractiveArea class containing 4 methods: _on_body_entered/exited and _on_area_entered/exited. Inserted via code. (Show below)
  • 3rd: There is no collision detected. I print() everywhere and there’s nothing triggered inside the procedural Area2D. But it works fine if I mask it on for the door physics collision.

Code

This is the function that creates the procedural Area2D. I’m at the point where I mash the keyboard hoping that what is added will fix the “problem,” but it isn’t working. Obviously. I’ve looked high and low on the interwebs but couldn’t find anything. Or I might just be blind. The Area2D is on Layer 3, as shown below.

I have to be forgetting something, but I need fresh eyes.

func make_interaction_area(doorPos : Vector2):
	var ia := InteractionArea.new()
	ia.action_name = "open"
	ia.action_param = doorPos
	ia.name = "IA_" + str(doorPos)
	var iaColl := CollisionShape2D.new()
	iaColl.shape = RectangleShape2D.new()
	iaColl.shape.size = Vector2(48, 48)
	var callable := Callable(gm, "_open_door")
	ia.collision_layer = 3
	ia.collision_mask = 0b00000000_00000000_00000000_00000010
	ia.interact = callable
	ia.connect("_on_area_entered", callable.bind(doorPos))
	ia.position = doorPos
	ia.add_child(iaColl)
	gm.add_child(ia)

Screenshot

Here’s a screenshot of what they look like in-game. The image is self-explanatory. If the player’s Area2D collision detection (orange) hits a door physics collision I get a print output, but nothing with the square at the base of the doors. Nothing from the walls either, but that’s because they’re on a different layer.

End

Help appreciated.
If you need more info/code, I’ll happily provide.

Thanks.

Just noted there’s an error output in the Debugger window. It says:

E 0:00:01:0925   room.gd:362 @ make_interaction_area(): In Object of type 'Area2D': Attempt to connect nonexistent signal '_on_area_entered' to callable 'Node2D(game.gd)::_open_door'.
  <C++ Error>    Condition "!signal_is_valid" is true. Returning: ERR_INVALID_PARAMETER
  <C++ Source>   core/object/object.cpp:1344 @ connect()
  <Stack Trace>  room.gd:362 @ make_interaction_area()
                 room.gd:285 @ paint()
                 TileMapGen.gd:281 @ make_map()
                 game.gd:60 @ make_dungeon()
                 gui_canvas.gd:5 @ make_dungeon()
                 ui.gd:5 @ _on_start_button_button_up()

What I am forgetting!?

Ermahgerd!

The signal is “area_entered” not “_on_area_entered.” And although the problem isn’t fixed (multiple parameters), at least there’s a (buggy) detection.

what else the problem here other than incorrect naming of the signal name?

Update

Alright.

This is now working except for one detail. The collision is detected on the player collision rectangle instead of the Area2D I set up for interacting purposes. (see below)

Here’s the new procedural collision:

New Code

func make_interaction_area(doorPos : Vector2):
	var ia := InteractionArea.new()
	ia.action_name = "open"
	ia.action_param = Vector2(doorPos.x, -doorPos.y)
	ia.name = "IA_" + str(doorPos)
	var iaColl := CollisionShape2D.new()
	iaColl.shape = RectangleShape2D.new()
	iaColl.shape.size = Vector2(40, 40)
	var callDoor := Callable(self, "_open_tm_door")
	var callEntered := Callable(ia, "_on_body_entered")
	var callExited := Callable(ia, "_on_body_exited")
	ia.collision_layer = 8
	ia.collision_mask = 0b00000000_00000000_00000000_00000010
	ia.interact = callDoor
	ia.connect("body_entered", callEntered)
	ia.connect("body_exited", callExited)
	ia.position = Vector2(doorPos.x + .5, doorPos.y - .5) * Globals.CellSize
	ia.add_child(iaColl)
	gm.add_child(ia)

Visual

The collision is only detected when the player collision (white arrow) enters the interactive area, but nothing is triggered where the red arrow points to enters the area.

Both the player collision and the player’s Area2D use the same collision layer (2). The player has 1, 3 and 4 as mask whereas the Area2D uses 3, 4 and 8.

The player scene is simple:
(Had to remove because of new user limitation. If needed I can post it in reply.)

I’m not sure where to look to explain why the proper area (orange box) isn’t triggering when they collide or am I misunderstanding how this works?

I’m starting to think I should use something else than an Area2D for the player’s interactive area. In fact, the more I think about this, the more it makes sense.

Another Update

I decided to play with the collision masks and layers and what seems to be happening is some sort of mix up.

  • With the post of the procedural collision above as a template: Layer still on 8, but mask changed to 7.
  • The PlayerDetector–shown in orange above–is on layer 7 and mask set to 3 and 4.

The PlayerDetector will detect the proc. collision and also the other way around.

  • PlayerDetector->ProcCollision :white_check_mark:
  • ProcCollision->PlayerDetector :white_check_mark:

If I remove the mask 4 from the PlayerDetector, that collision isn’t detected.

  • PlayerDetector->ProcCollision = NO DETECTION :x:
  • ProcCollision->PlayerDetector :white_check_mark:

My question is, why is 4 making it collide!? From everything I read, it should be set to mask 8 to interact with that collision area. Right?