TileMap map_to_local() doesn't consider scale?

Godot Version

4.2.1.stable.official [b09f793f5]


Hey everyone, Apologies in advance! I’m fairly new to Godot (and game development entirely) so this may be a dumb question.

I had the need to capture Area2D collisions with one of my TileMaps. Simplified example can be found below. What I’m noticing is that it appears the TileMap’s map_to_local() function doesn’t take into account any scale that may be applied. It took me awhile to come to this realization, and sure enough applying the second example below solves it. Is this intended behavior or a bug? I’m assuming this is the same side-effect as to why you aren’t supposed to scale your player collision shapes?

extends Node2D

@export var area: Area2D

func _ready():

func area_entered(_body_rid: RID, _body: Node2D, _body_shape_index: int, _local_shape_index: int):
	if(_body is TileMap):
        # position wrt the tilemap grid
		var tileMapPosition = _body.get_coords_for_body_rid(_body_rid)
		# position WITHOUT any scaling applied to TileMap
		var tilePositionWithoutScale = _body.map_to_local(tileMapPosition)
		# position WITH any scaling applied to TileMap
		var tilePositionWithScale = _body.map_to_local(tileMapPosition) * _body.scale

map_to_local takes a cell index and returns the *center* of the cell. it is like getting position of a child node. when you get a child node’s position, scale is not factored in.

		// tile size = 64,64

		GD.Print(v_tile_map.LocalToMap(new Vector2(128, 256)));
		//output: 2, 4
		GD.Print(v_tile_map.MapToLocal(new Vector2I(1,2)));
		// output: 96, 160
		// 96 = (64 * 1) + (64 / 2)
		// 160 = (64 * 2) + (64 / 2)

“To convert the returned value into global coordinates, use Node2D.to_global

1 Like

This affects my sprite selection since the scale identifies 4x as many sprites in the event.position area as actually selected.
Original sprite is 256x256. Scale is reduce to 0.25. Thus the actual size of the sprites are 64x64. But when multiple sprites are placed next to each other and only 1 is clicked on, print(“How big a click?”, $sprite2d.get_rect().size) still returns the original 256x256 click area and multiple sprites are affected by the click instead of the 1 sprite clicked.