#Area2D body_entered signal inconsistent when colliding with TileMapLayer

1 messages · Page 1 of 1 (latest)

latent mortar
#

My issue is very similar to https://discord.com/channels/1235157165589794909/1338574063207055360

In my case, I have a Player scene based on a CharacterBody2D. This Player has a child Area2D, called InteractArea2D. The goal is for this Area2D to detect collisions with a TileMapLayer so that the game is aware when the Player interacts with the tiles. Right now I simply want to know when this Area2D collides with any tile in the TileMapLayer.

It is working, but inconsistently. Like in the original post, I am seeing collisions detected only when the player jumps and not when it moves left or right. The Player animations do not effect the collision shape.

Also, when the player jumps. collisions are only detected when the Player is very close to the tile, instead of when the collision shape actually overlaps the tile.

latent mortar
#

In this video there should be the message "Player is to the right" when the player walks up to the tile, but there is nothing. Instead there is only a message when the player jumps while very close to the tile, and then only after it lands. Why doesn't it appear when the player walks up to it, or any time the small green box contacts the tile? You'll see in the video it does show the message just one single time when I switch directions. It's the only time it shows the message while moving left.

Instead of this craziness, it should only be showing the message any time the small green box contacts that dirt tile.

latent mortar
#

Okay, might be getting somewhere. I googled "godot 2d area2d body_entered update continuously" and was reminded that calling get_overlapping_bodies() in _physics_process helped in this situation. I was provided with the following useful code snippet:

extends Area2D

func _physics_process(_delta):
    # Get all bodies currently overlapping the area
    var bodies = get_overlapping_bodies()
    
    for body in bodies:
        if body.name == "Player":
            # Perform continuous action (e.g., deal damage over time)
            print("Player is still inside!")

I incorporated this and it helped to more consistently detect when the Area2D was colliding with the tile from any direction of movement, although it reports a lot and there is a small bug where once the Area2D exits the tile it incorrectly says I am on the opposite side of it.

Also, after closer debugging with the live game mode, I'm quite sure the distance issue is due to the origin point of the Area2D, because it's only detecting a collision when the middle of the green box is on the tile.

latent mortar
latent mortar
#

The RayCast solution is not much better. Still too many inconsistencies in this engine. Even though it recognizes collisions as the raycast is rotated, it will only correctly recognize the tile the player is in when the player is to the right left or above the tile. This is just nonsense how it will not consistently recognize the positions of things and I have to hack in solutions like "look one pixel to the left when the use presses left" because what live game collisions show is apparently not what the script will acknowledge.

solemn fossil
# latent mortar The RayCast solution is not much better. Still too many inconsistencies in this ...

So this is due to how (most) scripts will translate the collision point into tile coordinates. Let's say your tiles are 16x16px and you have a tile at (0,0) and raycast it from the right the collision point will be at (16, y). Your script will then try to find the tile coordinates by dividing by 16 and the result will be (1, 0).

That happens from the right and the bottom. I like to add a tiny bit of "length" to my raycast after it collides to ensure the collision point I calculate the tile coordinates from is inside the tile

#

I believe the safest option is actually to follow the collision normal inwards

if raycast.is_colliding():
  var point = raycast.get_collision_point()
  point += -raycast.get_collision_normal() * 0.1
  tile_coords = point / ....
latent mortar
#

Thanks a lot for looking into this. In my case, there is not a direct step of dividing by 16. Here's a link to the script that has the _physics_process function that is converting a local coordinate point to a tile coordinate: https://gitlab.com/aip-games-in-the-making/carbuncle-duplex/-/blob/94ce8c1d65ec7b75e85f478ee71be7aa1fea6ba8/scripts/player/tile_ray_cast_2d.gd#L18

I'm using a strategy I learned from this video https://www.youtube.com/watch?v=sACXaQj3JEQ which involves using TileMapLayer.local_to_map() https://docs.godotengine.org/en/stable/classes/class_tilemaplayer.html#class-tilemaplayer-method-local-to-map because the world_to_map function is no longer available.

Sorry that my project is a mess. It's an attempt to see a vision through, based on some amazing opengameart.org graphics: https://opengameart.org/content/simple-broad-purpose-tileset

Here is a video of the current behavior. I do think the raycast has to be extended or repositioned, but when the player is below a tile, the raycast is well within it so that I'm led to be believe the detection area is actually on the upper left side of the tiles, for some reason.

latent mortar
#

Sadly, I misspelled "Destructible" and will fix that, but here's a screenshot of how the editor is saying the location right below that first (soon to be) destructible tile is (-1, -1). This is the location where the red star is in the first screenshot.

In the second screenshot, the debugger is paused right after getting the player and target tile coordinates. The debugger values are showing that both the tile_coords and player_tile_coords are the same, which it only does when the player is to the right or below a tile.