I’m working on a mining game where I want to assign health to individual tiles so that players can break them over time. However, I’ve run into an issue with custom data in TileMap. It seems like the custom data is shared across all instances of the same tile type in the entire TileMap layer.
What I’m experiencing is that when I modify the health of a single tile, the change affects all tiles of the same type. For example, if I break one tile, then when I try to break another of the same type, it gets destroyed with just one hit, as if it inherited the modified health value from the first tile.
Is there a way to assign unique custom data to each individual tile so that each one can have its own health value? Or is there a different approach I should be taking to achieve this?
Thanks in advance for any help!
func action() -> void:
# Actualizamos informacion del collider
%RayCast2D.force_raycast_update()
# retornamos datos
var colliderData = %RayCast2D.get_collider()
# Verifica si el colisionador existe y pertenece al grupo "tile_map"
if colliderData and colliderData.is_in_group("tile_map"):
# Castea el colisionador a TileMap
var tileMap: TileMapLayer = colliderData as TileMapLayer
# Convierte la posición global del nodo sumada a la posición objetivo del RayCast2D
# a coordenadas de la grilla del TileMap
var tileCoordinate: Vector2 = tileMap.local_to_map(global_position + $RayCast2D.target_position)
# Obtiene los datos del tile en la coordenada calculada
var tileData: TileData = tileMap.get_cell_tile_data(tileCoordinate)
# Si los datos del tile existen
if tileData:
# Obtiene la salud actual del tile desde sus datos personalizados
var currentTileHealth: int = tileData.get_custom_data("health")
print_debug(currentTileHealth)
# Calcula la nueva salud del tile después de aplicar el daño
var newTileHealth = currentTileHealth - damage
# Actualiza la salud del tile con la nueva salud
##BUG: cambia el health de todos los tiles similares a este
tileData.set_custom_data("health", newTileHealth)
# Si la nueva salud del tile es menor o igual a 0
if newTileHealth <= 0:
# Remueve el tile en esa coordenada, desconectándolo del terreno
tileMap.set_cells_terrain_connect([tileCoordinate], 0, -1)
Hi everyone!
To solve this, I created a TileHealthManager class to keep track of the health for each tile based on its local position. Here’s the class I came up with:
class_name TileHealthManager extends Node2D
## Dictionary to store the health of each tile, using the local position as the key
var tile_health_data: Dictionary = {}
## Function to update the health of a tile
func update_tile_health(tile_position: Vector2, newHealth: int) -> void:
# Check if the tile already has a health record
if tile_position in tile_health_data:
# Update the health of the tile
tile_health_data[tile_position] = newHealth
## Function to set the initial health of a tile
func set_tile_health(tile_position: Vector2, health: int) -> void:
tile_health_data[tile_position] = health
## Function to get the current health of a tile, returns '-1' if the tile health is not found
func get_tile_health(tile_position: Vector2) -> int:
if tile_position in tile_health_data:
return tile_health_data[tile_position]
else:
return -1 # Return -1 if the tile health is not found
## Function to check if health data exists for a tile
func is_tile_health_exist(tile_position: Vector2) -> bool:
return tile_position in tile_health_data
## Function to remove the health data of a tile
func remove_tile_health(tile_position: Vector2) -> int:
if tile_position in tile_health_data:
if tile_health_data.erase(tile_position):
return 0 # Return 0 if the tile health data was successfully removed
return -1 # Return -1 if the tile health data was not found
In my tool script, I then use this TileHealthManager to manage the tile health as follows:
## Executes the action of the pickaxe.
func action() -> void:
# Update the RayCast2D to get fresh collider data
%RayCast2D.force_raycast_update()
# Get the collider data
var colliderData = %RayCast2D.get_collider()
# Check if the collider exists and belongs to the "tile_map" group
if colliderData and colliderData.is_in_group("tile_map"):
var tileMap: TileMapLayer = colliderData as TileMapLayer
var tileHealthManager: TileHealthManager = tileMap.get_parent()
# Get the local coordinates of the tile in the TileMap
var tileCoordinate: Vector2 = tileMap.local_to_map(global_position + $RayCast2D.target_position)
# Get the tile data at the calculated coordinates
var tileData: TileData = tileMap.get_cell_tile_data(tileCoordinate)
# If the tile data exists
if tileData:
# Check if the tile is destructible
if tileData.get_custom_data("destructible"):
# If health data does not exist for this tile, set it
if not tileHealthManager.is_tile_health_exist(tileCoordinate):
tileHealthManager.set_tile_health(tileCoordinate, tileData.get_custom_data("health"))
# Update the health of the tile
var currentTileHealth: int = tileHealthManager.get_tile_health(tileCoordinate)
currentTileHealth -= damage
if currentTileHealth > 0:
tileHealthManager.update_tile_health(tileCoordinate, currentTileHealth)
else:
# Remove the tile and its health data if health is 0 or less
tileHealthManager.remove_tile_health(tileCoordinate)
tileMap.set_cells_terrain_connect([tileCoordinate], 0, -1)