1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| extends TileMap # 定义了一个枚举。这是一种给数字起名字的方式(0,1,2),让代码更具可读性。 enum Tile { OBSTACLE, START_POINT, END_POINT }
# 定义瓦片尺寸(宽和高) const CELL_SIZE = Vector2i(64, 64) # 定义用于绘制路径的线条 const BASE_LINE_WIDTH = 3.0 # 宽度 const DRAW_COLOR = Color.WHITE * Color(1, 1, 1, 0.5) # 颜色
# 创建 AStarGrid2D 实例 # Godot 内置专门用于2D网格上进行路径寻找的工具,底层使用 A* 算法。 var _astar = AStarGrid2D.new()
# 用于存储路径计算的起点和终点在瓦片坐标系(Grid-based)位置 var _start_point = Vector2i() var _end_point = Vector2i() # 用于存储计算的路径上所有点的局部坐标系(Pixel-based)位置 var _path = PackedVector2Array()
func _ready(): # 定义 A* 算法工作的网格范围 # Rect2i(x, y, width, height) # 注意:这里的尺寸是硬编码的(18x10)如果地图大小会改变需要动态计算,例如通过 get_used_rect() _astar.region = Rect2i(0, 0, 18, 10) # 将 A* 网格的单元格大小设置为与瓦片大小一致 _astar.cell_size = CELL_SIZE # 将 A* 节点的位置设置在瓦片中心 _astar.offset = CELL_SIZE * 0.5 # 设置 A* 启发式函数为曼哈顿距离 _astar.default_compute_heuristic = AStarGrid2D.HEURISTIC_MANHATTAN _astar.default_estimate_heuristic = AStarGrid2D.HEURISTIC_MANHATTAN _astar.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVER # 禁止对角线移动 _astar.update() # 应用以上所有设置
# 遍历 _astar.region 定义的整个网格范围 for i in range(_astar.region.position.x, _astar.region.end.x): for j in range(_astar.region.position.y, _astar.region.end.y): var pos = Vector2i(i, j) if get_cell_source_id(0, pos) == Tile.OBSTACLE: _astar.set_point_solid(pos)
func _draw(): if _path.is_empty(): return
var last_point = _path[0] # 获取路径的第一个点 for index in range(1, len(_path)): # 从路径的第二个点开始遍历 var current_point = _path[index] draw_line(last_point, current_point, DRAW_COLOR, BASE_LINE_WIDTH, true) # 绘制线段 draw_circle(current_point, BASE_LINE_WIDTH * 2.0, DRAW_COLOR) # 绘制圆形 last_point = current_point # 更新 last_point,为下一次循环做准备
# 接收一个像素坐标,先将其转换为瓦片坐标 (local_to_map),然后再转换回像素坐标 (map_to_local)。 # 这样做的效果是,无论你传入哪个像素点,它都会返回该像素点所在瓦片的左上角像素坐标。 # 这常用于将一个自由移动的物体 “吸附” 到网格上。 func round_local_position(local_position): return map_to_local(local_to_map(local_position))
# 判断一个像素点所在的瓦片是否可以通过 func is_point_walkable(local_position): var map_position = local_to_map(local_position) if _astar.is_in_boundsv(map_position): return not _astar.is_point_solid(map_position) return false
# 用于清除上一次计算的路径 func clear_path(): if not _path.is_empty(): _path.clear() erase_cell(0, _start_point) erase_cell(0, _end_point) # 触发 _draw() 函数,清除屏幕上已绘制的路径线条和圆点 queue_redraw()
# 这是外部调用者最常用的函数,用于计算新路径 func find_path(local_start_point, local_end_point): clear_path() # 将传入的像素坐标转换为瓦片坐标 _start_point = local_to_map(local_start_point) _end_point = local_to_map(local_end_point)
# 调用 A* 算法,返回一个包含路径上所有点(像素坐标,且是瓦片中心)的 PackedVector2Array。如果找不到路径,该数组将为空。 _path = _astar.get_point_path(_start_point, _end_point)
if not _path.is_empty(): # 放置瓦片 set_cell(0, _start_point, 0, Vector2i(Tile.START_POINT, 0)) set_cell(0, _end_point, 0, Vector2i(Tile.END_POINT, 0))
# 触发 _draw() 函数,在屏幕上绘制出新的路径。 queue_redraw()
return _path.duplicate()
|