- Edited
It can be difficult to work with the new TileMaps if you want to interact with many tiles within the same map in different ways.
I have figured out a way to get the layer on which a collision occurred between something like a CharacterBody2D and a TileMap.
First you get the TileMap (collider) and the RID. Then you can get the collision layer of the tile you collided with using PhysicsServer2D.body_get_collision_layer().
var collision = move_and_slide()
if collision:
var collider = get_last_slide_collision().get_collider()
if collider is TileMap:
var tile_rid = get_last_slide_collision().get_collider_rid()
layer_of_collision = PhysicsServer2D.body_get_collision_layer(tile_rid)
Save this in a variable with node-level scope at the top of your script (layer_of_collision). I have two tiles in my TileMap that I want special behavior for. I have given them each their own collision layer and declared constants for them:
const PLATFORM_LAYER = 1 << (2 - 1) # collision layer 2
const ITEM_BLOCK_LAYER = 1 << (6 - 1) # collision layer 6
var layer_of_collision = null
Now that you have the collision layer, you can use it to validate actions.
Example 1: Check if a player can crouch fall through the tile. Temporarily alter the player's collision mask and re-enable it after a short time.
if crouched and layer_of_collision == PLATFORM_LAYER:
self.set_collision_mask_value(PLATFORM_LAYER, false)
$FallTimer.start()
func _on_fall_timer_timeout():
self.set_collision_mask_value(PLATFORM_LAYER, true)
Example 2: Change the tile of an item block the player has hit to a platform.
const TILE_LAYER = 0
const PLATFORM_TILE = Vector2i(0, 2) # TileMap atlas coordinates for platform tile
if layer_of_collision == ITEM_BLOCK_LAYER:
var tile_world_coords = collider.get_coords_for_body_rid(tile_rid)
var tile_source_id = collider.get_cell_source_id(TILE_LAYER, tile_world_coords, false)
if tile_source_id == -1:
layer_of_collision = null
return
collider.set_cell(TILE_LAYER, tile_world_coords, tile_source_id, PLATFORM_TILE)
Example 3: Get custom data from the tile's custom data layer with the name "type".
Use some of the same code from Example 2, then:
const NULL_TILE = Vector2i(-1, -1) # TileMap atlas coordinates for invalid tile
var tile_atlas_coords = collider.get_cell_atlas_coords(TILE_LAYER, tile_world_coords, false)
if tile_atlas_coords == NULL_TILE:
layer_of_collision = null
return
var tile_source = collider.tile_set.get_source(tile_source_id)
var tile_type = tile_source.get_tile_data(tile_atlas_coords, 0).get_custom_data("type")
Now you can use this data to take some other action.
Some of the included error checking may be unnecessary, but I left it in just in case.
I hope this information is useful for you.