godot_recipes icon indicating copy to clipboard operation
godot_recipes copied to clipboard

[Discussion] Tilemap: Detecting tiles

Open cbscribe opened this issue 4 years ago • 10 comments

Discussion for http://godotrecipes.com/2d/tilemap_collision/

cbscribe avatar Feb 03 '21 02:02 cbscribe

Thanks for the great tutorials and recipes. (They work really well for adults as well ;-) )

After some struggling, i learned that the TileMap::map_to_world resp. world_to_map are misleadingly named. They do not work with global coordinates, but local coordinates of the map. If the map is not scaled nor rotated and the origin at 0/0 (which is not unlikely for a tilemap), things work fine. But when the map is transformed, you actually need to translate the coordinates:

var tile_pos = collision.collider.world_to_map(collision.collider.to_local(position))

I think it would be worth it to mention that in the recipe somewhere.

dbu avatar Mar 01 '21 12:03 dbu

How about an update to include Tilemap collisions with Area2D, e.g. bullets, etc.

My solution is based on dodgy coding and guessing, but I expect there's a better way ;-)

chucklepie avatar Jun 06 '21 12:06 chucklepie

How about an update to include Tilemap collisions with Area2D

What do you mean? Areas detect TileMaps in the same way they detect any other body.

cbscribe avatar Jun 06 '21 16:06 cbscribe

@cbscribe, what I mean is you don't call move and collide/slide so there are no Colliders, as in on a kinematic you just get the collider then offset the normal. An area2d does not do this so you have to figure out how to project the area2d from its origin to the collision point in the direction it's facing/moving because the body entered doesn't pass the tile cell information.

chucklepie avatar Jun 06 '21 17:06 chucklepie

Well an Area2D does not collide, it overlaps. There is no point of collision, there is a region. This means your area can be touching multiple tiles at once. You have to come up with whatever logic you want to do to deal with that situation. Get the nearest tile to the area's position? Get all overlapping tiles and find the "middle" one? The answer can vary a lot depending on your needs and your setup.

cbscribe avatar Jun 06 '21 17:06 cbscribe

Yes, I know. What I was meaning is there is still a shape that raises the collision signal, exactly the same way your kinematic has a shape for collisions. So in most cases you want to know what tile hit your area 2d first.

On Sun, 6 Jun 2021, 18:46 Chris Bradfield, @.***> wrote:

Well an Area2D does not collide, it overlaps. There is no point of collision, there is a region. This means your area can be touching multiple tiles at once. You have to come up with whatever logic you want to do to deal with that situation. Get the nearest tile to the area's position? Get all overlapping tiles and find the "middle" one? The answer can vary a lot depending on your needs and your setup.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/kidscancode/godot_recipes/issues/19#issuecomment-855435496, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADCERRIFWIH3GHPZ7GRHWGDTROX5TANCNFSM4XAAEJKQ .

chucklepie avatar Jun 06 '21 19:06 chucklepie

thx! it really helped with disappearing tiles!

gamemake-eng avatar Jul 01 '21 19:07 gamemake-eng

Hi I just want to contribute with my use case, I'm doing a little breakout clone and I'm using a sphere collider for the ball so I had weird issues with the corners.

	# Check collisions with objects
	if collision:		
		# Did we hit a brick
		if collision.collider is TileMap:
			var brickMap: TileMap = collision.collider
			var tile_pos = brickMap.world_to_map(position)
			
			# If any normal component is different than 0 we know the tile is that way
			if collision.normal.y > 0:
				tile_pos.y -= 1
			if collision.normal.y < 0:
				tile_pos.y += 1
			if collision.normal.x > 0:
				tile_pos.x -= 1
			if collision.normal.x < 0:
				tile_pos.x += 1

			# This will remove the tile
			brickMap.set_cellv(tile_pos, -1)

My solution just uses the normal to know in which direction from the ball is the tile that I collided with

F1r3f0x avatar Jul 19 '21 00:07 F1r3f0x

So there's a pretty irritating issue with the TileMap editor, specifically with the tile ID returned by get_cellv. For starters, this value is not visible in the Inspector, only through the print command or opening up the .tscn in a text editor. The big problem is this: If you remove one of the tiles from a set, that ID is forever prevented from being used, if it is of a number lower than the highest in the set.

So, say you have a tileset with IDs 0, 1, 2, and 3. You then remove the tile with ID 2. If you add a new tile, it will be given ID 4. So now you have a tileset with IDs 0, 1, 3, and 4. If you're using the script in the example to subtract 1 from the tile ID, once it gets to 3, subtracting one from it will remove the tile from the map, instead of going to 1.

The only fix I've found is either creating an entirely new tileset (very carefully), or manually changing the ID values in the .tscn file with a text editor, and then reloading the entire project, and redrawing all the tiles that use the value that was changed. This approach is obviously not viable, especially if you're doing a cascading edit like changing 4->3, 3->2, etc.

There's probably a convoluted workaround using get_tile_ids() and a for loop, but I need to move on from this issue at the moment.

differenceclouds avatar Aug 08 '21 22:08 differenceclouds

Fantastic, just what I needed! Thanks for the elegant solution.

Battlefrog avatar Jul 26 '22 04:07 Battlefrog