Hex Tilesets in Godot 4.0

Godot 4.0 has brought with it sweeping changes to tilesets. In my opinion, the most exciting change is it’s quality of life changes to working with hex tilesets.

For this example, I am using a pack of hexagon tiles from the amazing Kenney.

Configuring a tileset to work with hexes

When creating a new tileset, under the Tile Shape menu, there’s now a “Hexagon” option.

Remember to change your tile size to match your hexes. (For the purpose of this example, we will use x: 120px, y: 140px.)

Configuring the tilemap

We are going to use two layers with our tilemap for this demo. Simply scoll down to the layers section of the Inspector for the tilemap and click “Add Element”. I have named mine for clarity.

Configuring the Tile Atlas

For our tileset texture, we need to configure our atlas to have a separation of x:2px, y:2px. Then, simply select the texture and let Godot import all the tiles for you!

Interacting with the TileMap from code

Highlighting a ring around a selected hex

The code is pretty simple;

  • Clear the layer
  • Convert the cursor’s screen coordinates to the map coordinates
  • Loop through a list of neighbour cells and set each cell to a tile from the Tile Atlas

	
func _input(event):
	if event is InputEventMouseButton:
		if (event as InputEventMouseButton).button_index == MOUSE_BUTTON_LEFT:
			tilemap.clear_layer(1)
			var map_mouse_pos = tilemap.local_to_map(get_global_mouse_position())
			for neighbour in tilemap.get_surrounding_cells(map_mouse_pos):
				tilemap.set_cell(1, neighbour, 0, Vector2i(6,2))

Highlighting an arc around a point (such as a player)

The code here is a little more complicated.

  • Clear the layer
  • Convert the cursor’s screen coordinates to the map coordinates
  • Check that the distance from the point on the map is equal to 1
  • Get a list of neighbours and for each neighbour, if the distance between it and the mouse position is equal to 1, set the cell to a tile from the Tile Atlas

	
# Artfully ripped from here: https://stackoverflow.com/a/18394812
func hex_distance(start:Vector2, dest:Vector2) -> int:
	var distance = max(max(abs(dest.y-start.y), 
												abs(ceil(dest.y / -2) + dest.x - ceil(start.y / -2) - start.x)),
												abs(-dest.y - ceil(dest.y / -2) - dest.x + start.y  + ceil(start.y / -2) + start.x))
	return distance

func _input(event):
	if event is InputEventMouseButton:
		if (event as InputEventMouseButton).button_index == MOUSE_BUTTON_LEFT:
			tilemap.clear_layer(1)
			var map_mouse_pos = tilemap.local_to_map(get_global_mouse_position())
			if hex_distance(map_mouse_pos, arc_origin) == 1:
				var dir = Vector2(map_mouse_pos - arc_origin)
				var mouse_arc = tilemap.get_surrounding_cells(map_mouse_pos)
				tilemap.set_cell(1, map_mouse_pos, 0, Vector2i(6,2))
				for point in mouse_arc:
					if hex_distance(Vector2(point), arc_origin) == 1:
						tilemap.set_cell(1, Vector2(point), 0, Vector2i(6,2))