Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Area2D reports TileMap in the signals, when collide with the tiles #24739

Open
Tracked by #45334
DragonFrai opened this issue Jan 3, 2019 · 10 comments
Open
Tracked by #45334

Area2D reports TileMap in the signals, when collide with the tiles #24739

DragonFrai opened this issue Jan 3, 2019 · 10 comments

Comments

@DragonFrai
Copy link

Godot version:
Godot 3.1-a4

OS/device including version:
Linux Mint 19.1
CPU: Intel Pentium G3250 (2) @ 3.200GHz
GPU: NVIDIA GeForce GTX 750

Issue description:
Area2D, when overlapping with the TileMap, sent a signals (body_centered, body_exited, body_shape_entered, body_shape_exited). But as the 'body' argument sends a TileMap. Maybe instead it should sent StaticBody2D / KinematicBody2D depending on the settings of the TileMap?

Steps to reproduce:
Collide Area2D and Tile with CollisionShape. Area2D signals will contain TileMap.

Minimal reproduction project:
CollideArea2DAndTileMap.zip

@Zylann
Copy link
Contributor

Zylann commented Jan 3, 2019

TileMaps don't instance nodes for every single tile. Tiles are not individual nodes, that's why your area does not report StaticBody2D (and would never report KinematicBody2D), and report TileMap instead. It's just the way it works.

@DragonFrai
Copy link
Author

DragonFrai commented Jan 3, 2019

But the signal handler method of body_entered and others signals Area2D expects PhysicsBody2D. But he gets TileMap. They are only linked by Node2D, which makes type casting strange.

I implemented a projectile based on Area2D. The projectile must ignore all bodies with OneWayCollision. I want to get a CollisionShape2D or CollisionRegion2D with which body the collision occurred. Or somehow else get OneWayCollision. I would not like to make platforms as separate scenes.

@timoschwarzer
Copy link
Contributor

I think this is a documentation issue here as the body parameters in Area2D signals obviously aren't always PhysicsBody2D.

@ekliot
Copy link

ekliot commented Jan 4, 2019

I'm having a similar issue with this in 3.1 alpha 5, where I'm trying to use the body_shape_entered signal for an Area2D. Here is the signal's signature: body_shape_entered( int body_id, PhysicsBody2D body, int body_shape, int area_shape )

Here's what I get from the signal: 1268, [TileMap:1268], 68, 0

The interesting part here is the body_shape arg. In lieu of actually solving the root problem, I've got a bit of a workaround working here:

func _save_collision_data(body_id:int, body:TileMap, body_shape:int, area_shape:int) -> void:
  var tset: TileSet= body.tile_set

  var cellv: Vector2 = body.get_used_cells()[body_shape]
  var tile_id: int = body.get_cellv(cellv)
  var auto_coord: Vector2 = body.get_cell_autotile_coord(cellv.x, cellv.y)

  for shape_data in tset.tile_get_shapes(tile_id):
    if shape_data['autotile_coord'] == auto_coord:
      print(shape_data) # {autotile_coord:(3, 0), one_way:False, shape:[ConvexPolygonShape2D:1174], shape_transform:((1, 0), (0, 1), (0, 0))}

Note, the transform, and the points of the shape, are both kinda useless on their own. You can use TileMap.map_to_world() to help get global coords for the Shape2D.

This is a bit cumbersome. But, in case you want to get a Shape2D from collision data and those aren't problems for you, the above workaround could be helpful if baked into a script attached to the TileMap:

# filename: my_tile_map.gd
extends TileMap

func get_shape_data(tile_idx:int) -> Dictionary:
  var cellv := get_used_cells()[tile_idx]
  var tile_id := get_cellv(cellv)
  var auto_coord := get_cell_autotile_coord(cellv.x, cellv.y)

  for shape_data in tile_set.tile_get_shapes(tile_id):
    if shape_data['autotile_coord'] == auto_coord:
      return shape_data

EDIT obviously, change as needed if you're not using autotiles

Nice-to-haves would be to not have to iterate over all the shapes in the TileSet. I couldn't find a good way to get the exact index of the given tile in that shapes array.

Of course, this doesn't solve the root issue: the signal signature states it will give a PhysicsBody2D, which in the case of a TileMap it clearly doesn't.

EDIT

I've continued testing this and I think there's either something broken in TileMap, or my understanding of the interface.

Given a pos vector from body.get_used_cells()[body_shape], the result of body.map_to_world(cellv) does not give a coordinate that the Area2D is anywhere near. Instead, the coordinates are always at the top-left of the TileMap. I've tried using TileMap.world_to_map() using a coord from ray-casting to a tile in the Area2D, and the resulting pos vector is starkly different from the one given by body.get_used_cells(). I even tried reversing the used cells array, on the off chance it might be in reverse order.

Looking at the source, I don't think the Array is returned in any particular order, not least of all in regards to the body_shape arg for the collision signal.

I take back my earlier suggestion until I can be certain the body_shape arg is useful for anything. Meanwhile, TileMap.world_to_map() may prove useful.

@MarianoGnu
Copy link
Contributor

TileMap instances internally an StaticBody2d or KinematicBody2D depending on the property setup. This like this since Godot1.0
@reduz should know a little more, but as far as i can tell, this is an expected behaviour

@Zylann
Copy link
Contributor

Zylann commented Jan 12, 2019

@MarianoGnu to clarify, do you mean actual StaticBody2D and KinematicBody2D nodes, or PhysicsServer objects? From what I see in the code, I think Tilemap only deals with server RIDs (I didn't know tilemaps could have kinematic bodies Oo)

@MarianoGnu
Copy link
Contributor

TileMap instances bodies inside the PhysicsServer, there's not a node for this (in fact i belive it instances a body per space, the space is divided on quadrants to reduce the collision tests at minimum)

@Anaxagor14
Copy link

Given a pos vector from body.get_used_cells()[body_shape], the result of body.map_to_world(cellv) does not give a coordinate that the Area2D is anywhere near. Instead, the coordinates are always at the top-left of the TileMap.

I think I found out why the coordinates are wrong. body_shape is quadrant-based! I have created an issue with my investigations on the matter: #26138

@akien-mga akien-mga added this to the 3.2 milestone Apr 15, 2019
akien-mga added a commit to akien-mga/godot that referenced this issue Jun 24, 2019
Those signals receive either a PhysicsBody2D or a TileMap object,
and what the emitting method checks internally is only that the
object is a Node. In theory any Node could go through these signals
if they talk directly to the PhysicsServer2D.

Also updated docs.

Fixes godotengine#27076.

Might need further (compat breaking) improvement as this API is a
bit confusing, cf. godotengine#24739.
@akien-mga akien-mga modified the milestones: 3.2, 4.0 Jun 24, 2019
@akien-mga
Copy link
Member

See #30043 (comment).

This can likely be fixed properly for 4.0, so assigning to that milestone.

myhalibobo pushed a commit to myhalibobo/godot that referenced this issue Sep 3, 2019
Those signals receive either a PhysicsBody2D or a TileMap object,
and what the emitting method checks internally is only that the
object is a Node. In theory any Node could go through these signals
if they talk directly to the PhysicsServer2D.

Also updated docs.

Fixes godotengine#27076.

Might need further (compat breaking) improvement as this API is a
bit confusing, cf. godotengine#24739.
@furroy
Copy link

furroy commented May 3, 2021

Anyone struggling with this, I found a work around to do what I needed here: godotengine/godot-proposals#2543 Don't forget to copy your collision bits to the parent when you make it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests