Hi everyone I am working with Godot 4.3 and struggling with the new tile system while trying to "highlight" a tile when hovered over with the cursor. I want to replace a cell's visual appearance without actually removing or replacing the cell data.
I've used the following function to manipulate tiles:
void set_cell(int layer, Vector2i coords, int source_id = -1, Vector2i atlas_coords = Vector2i(-1, -1), int alternative_tile = 0)
However, my implementation seems to remove the cell instead of just highlighting it. Here's the relevant part of my code:
extends TileMap
var highlighted_tile_position: Vector2i = Vector2i(-1, -1)
var highlight_tile_id:Vector2i = get_cell_atlas_coords(layer, Vector2i(0, 1)) # Ensure this is a valid and visible tile ID
func _process(delta: float):
var mouse_pos = to_local(get_global_mouse_position())
var tile_pos = local_to_map(mouse_pos)
if tile_pos != highlighted_tile_position:
# Reset the previously highlighted tile if valid
if highlighted_tile_position != Vector2i(-1, -1) and get_cell_source_id(layer, highlighted_tile_position) != -1:
set_cell(layer, highlighted_tile_position, get_cell_source_id(layer, highlighted_tile_position)) # Reset to its original ID
# Highlight the new tile if valid
if get_cell_source_id(layer, tile_pos) != -1:
highlighted_tile_position = tile_pos
set_cell(layer, tile_pos, 0, highlight_tile_id) # Attempt to highlight new tile
The issue seems to be in the way I'm using set_cell as it erases the tile instead of highlighting it. I suspect my source_id, atlas_coords, and alternative_tile parameters might be incorrect.
Questions:
How do I correctly set these parameters to change the tile's appearance without removing the underlying data? Is there a more appropriate way to temporarily alter a tile's appearance on hover? Any insights or examples would be greatly appreciated.
Setting cells via set_cell()
works best when you want your terrain to change dynamically at runtime in response to in-game events (such as explosions altering the game field) because the engine uses the data stored for each tile for auto-tiling, collisions, and physics interaction.
On the other hand, a marker that highlights tiles where the mouse cursor is only serves visual purposes; it doesn't belong to the tilemap entity but to the user interface domain.
Since you want to preserve underlying tile data, I strongly recommend treating such marker as a separate, independent Node. This way, you can control its behaviour, change its appearance, animate it, and much more.
I propose a solution where your TileMap
(or TileMapLayer
, the actual "new tile system") controls a child Node acting as a highlight marker and following the mouse cursor while snapping to the tilemap grid:
extends TileMap
@onready var marker: Node2D = get_node("Marker") # Child Node2D
func _process(delta: float):
var mouse_pos = get_local_mouse_position()
var cell_pos = local_to_map(mouse_pos)
highlight_cell(cell_pos)
func highlight_cell(cell_position: Vector2i):
marker.position = map_to_local(cell_position)
The marker Node can be anything you want: Sprite2D
, AnimatedSprite2D
, or even a plain Node2D
with some custom drawing such as:
extends Node2D
@export var size := Vector2.ONE * 14.0
@export var color := Color(1.0, 1.0, 0.0, 0.5)
func _draw():
draw_rect(Rect2(-size / 2.0, size), color)
Here's the result (tileset credits: Happyland by Buch on OGA):