Beginner seeking help with hexagonal tile based map

Godot Version

4.3.stable

Question

I’ve been messing around with godot for about a week now and I’ve been looking at just about anything I can find on google related to hexagonal tile maps in 3D. I created a learning environment of sorts that I need help with so I can teach myself (with chatgpt) how to implement pathfinding, collisions, and eventual height traversal. Currently I have the script to make a map but cant seem to figure out how to get a coordinate system working. Can anyone provide some advice on how to handle this? I’m also trying to get the coordinates of each hex shown (currently using label3d) on top of each tile if anyone has advice on this as well.

Learning godot from scratch seems to be the equivalent of drinking from a fire hydrant.

I have 2 scripts for the map.

extends Node3D

# Grid coordinates for the hex cell
var coordinates: Vector2 = Vector2.ZERO

# Set the coordinates of the cell
func set_coordinates(x: int, z: int) -> void:
	coordinates = Vector2(x, z)
	update_label_text()

# Set the color of the cell
func set_color(color: Color) -> void:
	if $MeshInstance3D.material_override is StandardMaterial3D:
		$MeshInstance3D.material_override.albedo_color = color

# Update the Label3D text to display grid coordinates
func update_label_text() -> void:
	# Ensure the Label3D node exists and update its text
	if $Label3D:
		$Label3D.text = "({}, {})".format(str((coordinates.x)), str((coordinates.y)))


# Optional: Identify this node as a hex cell
func is_hex_cell() -> bool:
	return true

and

extends Node3D  # Ensure the root extends Node3D for 3D environments

# Constants for hexagon metrics
const OUTER_RADIUS: float = 1
const INNER_RADIUS: float = OUTER_RADIUS * 0.866025404
const CORNERS: Array[Vector3] = [
	Vector3(0, 0, OUTER_RADIUS),
	Vector3(INNER_RADIUS, 0, OUTER_RADIUS * 0.5),
	Vector3(INNER_RADIUS, 0, -OUTER_RADIUS * 0.5),
	Vector3(0, 0, -OUTER_RADIUS),
	Vector3(-INNER_RADIUS, 0, -OUTER_RADIUS * 0.5),
	Vector3(-INNER_RADIUS, 0, OUTER_RADIUS * 0.5)
]

# Grid configuration
@export var grid_width: int = 6
@export var grid_height: int = 6

# Colors for cells
@export var default_color: Color = Color(1, 1, 1)
@export var touched_color: Color = Color(1, 0, 0)

# Packed scene for HexCell
@export var cell_scene: PackedScene  # Drag `HexCell.tscn` here in the editor

# Storage for cells
var cells: Array = []

# Called when the node is added to the scene
func _ready() -> void:
	create_grid()

# Create the grid of hexagonal cells
func create_grid() -> void:
	for z in range(grid_height):
		for x in range(grid_width):
			create_cell(x, z)

# Create a single hex cell at a specific grid position
func create_cell(x: int, z: int) -> void:
	# Create a new instance of the HexCell scene
	var cell = cell_scene.instantiate()
	add_child(cell)  # Add the cell to the HexGrid node

	# Calculate position based on hexagonal grid layout
	var cell_position = Vector3(
		(x + z * 0.5 - int(z / 2)) * INNER_RADIUS * 2,  # X position
		0,                                             # Y position (flat grid, no height for now)
		z * OUTER_RADIUS * 1.5                         # Z position
	)
	cell.position = cell_position  # Set the position of the Node3D root

	# Set cell properties
	cell.set_coordinates(x, z)  # Assign coordinates
	cell.set_color(default_color)  # Assign default color

	# Add the cell to the list for future reference
	cells.append(cell)

# Handle mouse input for interacting with the grid
func _input(event: InputEvent) -> void:
	if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
		var cell = get_cell_under_mouse()
		if cell:
			cell.set_color(touched_color)

# Get the hex cell under the mouse cursor using raycasting
func get_cell_under_mouse() -> Node:
	var camera = get_viewport().get_camera_3d()
	if not camera:
		return null
	var mouse_pos = get_viewport().get_mouse_position()
	var ray_origin = camera.project_ray_origin(mouse_pos)
	var ray_dir = camera.project_ray_normal(mouse_pos)

	# Create the ray query
	var ray_query = PhysicsRayQueryParameters3D.new()
	ray_query.from = ray_origin
	ray_query.to = ray_origin + ray_dir * 1000

	# Perform the raycast
	var result = get_world_3d().direct_space_state.intersect_ray(ray_query)

	if result and result.collider.has_method("is_hex_cell"):
		return result.collider
	return null

here is a snip of it running

Please update your post to use preformatted text with ``` for code snippets, otherwise it’s barely readable.

1 Like

Thank you for replying. Did my edits fix the bad posting etiquette?

1 Like