- Edited
Hi im trying to make a 2d hexgrid movement but i cant get it to work. I have set the tile terrain cost with Tileset ID. But the player doesnt always take the path with the least movementpoint and sometimes takes "shortcuts" via the corners of the hex.
I dont know how to fix this.
Here is my code:
GridMovement.gd
extends Node2D
@onready var tilemap = $TileMap
@onready var player = $Player
@onready var path_preview = $PathPreview
@onready var debug_label = $CanvasLayer/DebugLabel
const MAX_MOVE_POINTS := 10
var player_move_points := MAX_MOVE_POINTS
var move_speed := 100.0
var selected_tile: Vector2i = Vector2i(-999, -999)
var preview_path: Array = []
var move_path: Array = []
var move_index := 0
var hex_astar = preload("res://Scripts/a_star_hex_grid_2d.gd").new()
func _ready():
await get_tree().process_frame
hex_astar.setup_hex_grid(tilemap, 0)
var start_tile = Vector2i(-1, -1)
player.global_position = tilemap.to_global(tilemap.map_to_local(start_tile))
debug_label.text = " Debug init"
func _input(event):
if event.is_action_pressed("end_turn"):
start_new_turn()
func _unhandled_input(event):
if move_index < move_path.size():
return
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
var clicked_tile = tilemap.local_to_map(tilemap.get_local_mouse_position())
var player_tile = tilemap.local_to_map(tilemap.to_local(player.global_position))
var debug_text = "
Klickat på tile: " + str(clicked_tile)
debug_text += "\n
Spelare står i tile: " + str(player_tile)
var path = find_path(player_tile, clicked_tile)
if path.is_empty():
debug_text += "\n
Ingen giltig path – blockerad eller ogiltig"
clear_preview_visuals()
preview_path.clear()
selected_tile = Vector2i(-999, -999)
else:
debug_text += "\n
Path längd: " + str(path.size())
var cost = calculate_path_cost(path)
debug_text += "\n
Kostnad: " + str(cost) + " / " + str(player_move_points)
if cost > player_move_points:
debug_text += "\n
Inte tillräckligt med move points!"
elif clicked_tile == selected_tile and preview_path.size() > 0:
debug_text += "\n
Path bekräftad – startar rörelse"
move_along_path(preview_path.duplicate())
player_move_points -= cost
clear_preview_visuals()
preview_path.clear()
selected_tile = Vector2i(-999, -999)
else:
debug_text += "\n
Visar path – klicka igen för att gå"
clear_preview_visuals()
preview_path = path
selected_tile = clicked_tile
show_preview_visuals(preview_path)
debug_label.text = debug_text
func _process(delta):
if move_index >= move_path.size():
return
var target = move_path[move_index]
var direction = target - player.global_position
var distance = direction.length()
if distance < 2.0:
player.global_position = target
move_index += 1
if move_index >= move_path.size():
player.play_idle_animation()
else:
var step = direction.normalized() * move_speed * delta
player.global_position += step
player.play_walk_animation(step)
func find_path(start: Vector2i, goal: Vector2i) -> Array:
print(" Söker path från ", start, " till ", goal)
var raw_path: PackedVector2Array = hex_astar.get_path(start, goal)
var from_id = hex_astar.coords_to_id(start)
var to_id = hex_astar.coords_to_id(goal)
print(" Start ID:", from_id, ", Mål ID:", to_id)
if from_id == -1:
print(" Start-tile finns inte i AStar-nätet:", start)
if to_id == -1:
print(" Mål-tile finns inte i AStar-nätet:", goal)
var converted: Array = []
for pos in raw_path:
converted.append(tilemap.local_to_map(pos)) # hex-grid tile-coords
print("
Path: ", converted)
return converted
func calculate_path_cost(path: Array) -> int:
var total_cost = 0
for tile in path:
var tile_id = tilemap.get_cell_source_id(0, tile)
print("Clicked tile ID:", tile_id, " at coords: ", tile)
var cost = get_tile_cost(tile_id)
if tile_id == -1 or cost == -1:
return 9999
total_cost += cost
return total_cost
func get_tile_cost(tile_id: int) -> int:
match tile_id:
2: return 1 # Gräs
3: return -1 # Berg
4: return 2 # Skog
5: return -1 # Vatten
6: return 3 # Träsk
7: return 2 # Sand
8: return 1 # Kullar
9: return 1 # Vägar
10: return 2 # Snö
11: return -1 # Floder
_: return 1
func move_along_path(path: Array):
move_path.clear()
for tile in path:
move_path.append(tilemap.to_global(tilemap.map_to_local(tile)))
move_index = 0
func start_new_turn():
player_move_points = MAX_MOVE_POINTS
debug_label.text += "\n Ny tur – move points återställda till " + str(player_move_points)
func clear_preview_visuals():
for child in get_children():
if child.name.begins_with("preview_marker") or child.name.begins_with("step_label"):
child.queue_free()
func show_preview_visuals(path: Array):
for i in path.size():
var tile = path
var pos = tilemap.to_global(tilemap.map_to_local(tile))
var marker = ColorRect.new()
marker.name = "preview_marker_%d" % i
marker.color = Color(1, 1, 1, 0.4)
marker.size = Vector2(10, 10)
marker.position = pos - marker.size * 0.5
add_child(marker)
var label = Label.new()
label.name = "step_label_%d" % i
label.text = str(i)
label.modulate = Color.BLACK
label.position = pos - Vector2(5, 20)
add_child(label)
path_preview.update_path(path, tilemap)
a_star_hex_grid_2d.gd
extends AStar2D
class_name AStarHexGrid2D
var tile_map: TileMap
var solid_data_name := "solid"
var coords_to_id_map := {}
Setup the grid
func setup_hex_grid(passed_tile_map: TileMap, layer: int) -> void:
tile_map = passed_tile_map
coords_to_id_map.clear()
clear()
var used_cells = tile_map.get_used_cells(layer)
for cell in used_cells:
var tile_data = tile_map.get_cell_tile_data(layer, cell)
var is_tile_solid = tile_data.get_custom_data(solid_data_name)
if not is_tile_solid:
var local_pos = tile_map.map_to_local(cell)
var cost = get_tile_cost_by_coords(cell)
var id = coords_to_id_map.size()
coords_to_id_map[cell] = id
add_point(id, local_pos, cost)
print("
Tillagd tile:", cell)
else:
print("
Blockerad tile:", cell)
# Anslut alla gångbara noder till sina riktiga hexgrannar
for cell in coords_to_id_map.keys():
var center_id = coords_to_id_map[cell]
for neighbor in get_hex_neighbors(cell):
if coords_to_id_map.has(neighbor):
var neighbor_id = coords_to_id_map[neighbor]
connect_points(center_id, neighbor_id)
func get_hex_neighbors(cell: Vector2i) -> Array:
var neighbors := []
var x = cell.x
var y = cell.y
var directions_even_x = [
Vector2i(+1, 0), Vector2i(0, -1), Vector2i(-1, -1),
Vector2i(-1, 0), Vector2i(-1, +1), Vector2i(0, +1)
]
var directions_odd_x = [
Vector2i(+1, 0), Vector2i(+1, -1), Vector2i(0, -1),
Vector2i(-1, 0), Vector2i(0, +1), Vector2i(+1, +1)
]
var directions = directions_even_x if x % 2 == 0 else directions_odd_x
for dir in directions:
neighbors.append(cell + dir)
return neighbors
func get_tile_cost_by_coords(cell: Vector2i) -> float:
var tile_id = tile_map.get_cell_source_id(0, cell)
match tile_id:
2: return 1.0 # Gräs
3: return INF # Berg (blockerad)
4: return 2.0 # Skog
5: return INF # Vatten (blockerad)
6: return 3.0 # Träsk
7: return 2.0 # Sand
8: return 1.0 # Kullar
9: return 1.0 # Vägar
10: return 2.0 # Snö
11: return INF # Flod (blockerad)
_: return 1.0 # Standard
func coords_to_id(coords: Vector2i) -> int:
if coords_to_id_map.has(coords):
return coords_to_id_map[coords]
return -1
func get_path(from_point: Vector2i, to_point: Vector2i) -> PackedVector2Array:
var from_id = coords_to_id(from_point)
var to_id = coords_to_id(to_point)
if from_id == -1 or to_id == -1:
print("
Invalid path request – from_id:", from_id, " to_id:", to_id)
return PackedVector2Array()
return get_point_path(from_id, to_id)
PathPreview.gd
extends Node2D
class_name PathPreview
var path_points: Array = []
var tilemap: TileMap
func _draw():
if path_points.size() < 2 or tilemap == null:
return
for i in range(path_points.size() - 1):
var from_tile = tilemap.local_to_map(tilemap.to_local(path_points[i]))
var to_tile = tilemap.local_to_map(tilemap.to_local(path_points[i + 1]))
var from_pos = tilemap.to_global(tilemap.map_to_local(from_tile))
var to_pos = tilemap.to_global(tilemap.map_to_local(to_tile))
draw_line(from_pos, to_pos, Color.RED, 2.0)
func update_path(path: Array, tilemap_ref: TileMap):
path_points = path
tilemap = tilemap_ref
queue_redraw()
player.gd
extends Node2D
@onready var anim = $AnimatedSprite2D
var last_direction: Vector2 = Vector2.ZERO
func _ready():
anim.play("front_idle")
func play_walk_animation(direction: Vector2):
if direction == Vector2.ZERO:
play_idle_animation()
return
last_direction = direction
if abs(direction.x) > abs(direction.y):
# Horisontell rörelse
anim.flip_h = direction.x < 0
anim.play("side_walk")
else:
# Vertikal rörelse
anim.flip_h = false
if direction.y > 0:
anim.play("front_walk")
else:
anim.play("back_walk")
func play_idle_animation():
if abs(last_direction.x) > abs(last_direction.y):
anim.play("side_idle")
else:
if last_direction.y > 0:
anim.play("front_idle")
else:
anim.play("back_idle")
Thanks for any help // Linus