How to detect the tile id when a KinematicBody2D collides with a TileMap?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Matt Greer

This is a fairly common question both here and in other places like Reddit. But I am a bit stuck as the commonly proposed solution does not seem to work for me.

When a KinematicBody2D collides with a tile in a TileMap, I want to get that tile’s id. Here is how I am doing this:

extends KinematicBody2D

var velocity = Vector2.ZERO

func _physics_process(_delta):
	velocity.x = 0
	velocity.y = 100

	if Input.is_action_pressed("ui_left"):
		velocity.x = -60

	if Input.is_action_pressed("ui_right"):
		velocity.x = 60

	velocity = move_and_slide(velocity, Vector2.UP)

	for i in range(get_slide_count()):
		var collision = get_slide_collision(i)

		if collision.collider is TileMap:
			var cell = collision.collider.world_to_map(collision.position - collision.normal)
			var tileId = collision.collider.get_cellv(cell)
			print("tileId ", tileId)

The commonly proposed solutions seems to rely on tilemap.world_to_map(collision.position - collision.normal)

But I am finding this only works if the collision occurs on the left side of the tile. If it occurs on the right half, I end up with -1 as the tile id.

I made a tiny Godot project that shows the issue I am having:

https://github.com/city41/GodotTileMapCollisionTest

If you run that project, then hold down the left arrow key, you’ll see in the output console the tile id go from -1 to 0. I am expecting it to always be 0.

The above code is from this project. My actual project has more complex code, but ultimately seems to have the same problem.

Can anyone tell me what I am doing wrong?

I am using Godot 3.2.2.stable

I’ve created a pull request in your github with demonstration of my answer below.

Reloecc | 2020-09-12 14:52

:bust_in_silhouette: Reply From: Reloecc

Main issue with your way is that the get_cellv doesn’t do what you think it does. It returns the index of the sprite in the tileset. Not an index of the tile in tilemap.

Plus: Be aware that you won’t get multiple slide detections with one tilemap (demonstrated by creating multiple tiles and collide markers).

The index of the sprite in the tileset is what I am interested in. I want to know what type of tile has been touched. We discussed this already in the PR (thanks again). But it looks like what I want to do might not be possible, so I am exploring other options.

Basically what happens is Godot will tell me there has been a collision with the tilemap, but world_to_map(collision.position) does not return the correct tile coordinate in all circumstances. A collision with the tile at (3, 1) happens, but world_to_map(collision.position) returns (4, 1) if the collision happens to the far right side of the tile.

I wonder if I should submit a github issue for this? I am very new to Godot, so I have a lot to learn. But this feels like it might be a bug?

Matt Greer | 2020-09-12 15:08

I think the solution is to just subtract Vector2(1, 0) from collision.position. This seems to work because Godot seems to bias the collision towards the right side of the kinematic body.

Thanks to njamster for the idea. He mentioned it in this thread which is also about kinematic bodies and tilemaps: https://forum.godotengine.org/61590/how-would-i-detect-a-collition-on-a-specific-tile-type

Matt Greer | 2020-09-13 22:48