Godot4.2实战:用AStar2D + 自定义权重,轻松实现《文明》式六边形网格地图寻路

发布时间:2026/6/1 4:24:34

Godot4.2实战:用AStar2D + 自定义权重,轻松实现《文明》式六边形网格地图寻路 Godot4.2实战用AStar2D 自定义权重轻松实现《文明》式六边形网格地图寻路在策略游戏开发中六边形网格Hex Grid因其独特的空间表现力和战术深度成为《文明》系列等经典游戏的标配。但如何在这种非矩形网格上实现带地形权重的智能寻路本文将带你用Godot4.2的AStar2D从零构建完整的六边形寻路系统。1. 六边形网格的数学基础六边形网格与常见的方形网格不同其坐标系统和邻接关系需要特殊处理。我们采用轴向坐标系Axial Coordinates表示六边形位置通过q水平轴和r对角轴两个参数定位每个单元格。六边形网格的核心特性包括六个邻接方向每个六边形单元格有6个直接相邻单元三种坐标布局平顶Flat-top或尖顶Pointy-top两种常见布局距离计算两单元格间距离(abs(q1 - q2) abs(q1 r1 - q2 - r2) abs(r1 - r2)) / 2以下是将轴向坐标转换为屏幕像素坐标的关键代码# 平顶六边形布局的坐标转换 func axial_to_pixel(q: int, r: int, size: float) - Vector2: var x size * (3.0/2 * q) var y size * (sqrt(3)/2 * q sqrt(3) * r) return Vector2(x, y)2. 构建六边形AStar2D图在Godot中实现六边形寻路需要解决三个核心问题2.1 点ID映射系统为每个六边形创建唯一ID建议使用q * 1000 r的编码方式避免冲突func get_hex_id(q: int, r: int) - int: return q * 1000 r # 假设r 10002.2 邻接关系建立六边形网格的连接需要特殊处理以下是平顶六边形的六个邻接方向const HEX_DIRECTIONS [ Vector2i(1, 0), Vector2i(1, -1), Vector2i(0, -1), Vector2i(-1, 0), Vector2i(-1, 1), Vector2i(0, 1) ] func connect_hex_neighbors(astar: AStar2D, q: int, r: int): var center_id get_hex_id(q, r) for dir in HEX_DIRECTIONS: var neighbor_q q dir.x var neighbor_r r dir.y var neighbor_id get_hex_id(neighbor_q, neighbor_r) if astar.has_point(neighbor_id): astar.connect_points(center_id, neighbor_id, false)2.3 地形权重系统不同地形应设置不同的移动成本这是策略游戏的核心机制地形类型移动成本视觉颜色平原1.0#7CFC00森林1.5#228B22山地3.0#8B4513沼泽2.0#556B2F道路0.7#CD853F实现代码示例enum TerrainType { PLAIN, FOREST, MOUNTAIN, SWAMP, ROAD } const TERRAIN_WEIGHTS { TerrainType.PLAIN: 1.0, TerrainType.FOREST: 1.5, TerrainType.MOUNTAIN: 3.0, TerrainType.SWAMP: 2.0, TerrainType.ROAD: 0.7 } func add_terrain_point(astar: AStar2D, q: int, r: int, terrain: TerrainType): var point_id get_hex_id(q, r) var position axial_to_pixel(q, r, CELL_SIZE) var weight TERRAIN_WEIGHTS[terrain] astar.add_point(point_id, position, weight)3. 完整实现流程3.1 初始化六边形网格extends Node2D const CELL_SIZE 64 var astar AStar2D.new() var terrain_map {} # 存储每个六边形的地形类型 func _ready(): generate_hex_grid(10, 10) # 10x10的六边形网格 func generate_hex_grid(width: int, height: int): for q in range(width): for r in range(height): # 随机分配地形类型 var terrain randi() % TerrainType.size() terrain_map[get_hex_id(q, r)] terrain add_terrain_point(astar, q, r, terrain) # 建立邻接关系 for q in range(width): for r in range(height): connect_hex_neighbors(astar, q, r)3.2 可视化绘制func _draw(): # 绘制六边形网格 for id in astar.get_point_ids(): var pos astar.get_point_position(id) var terrain terrain_map[id] draw_hex(pos, CELL_SIZE, TERRAIN_COLORS[terrain]) # 绘制寻路路径 if has_path: for i in range(path.size() - 1): draw_line(path[i], path[i1], Color.RED, 3) func draw_hex(center: Vector2, size: float, color: Color): var points [] for i in range(6): var angle_deg 60 * i var angle_rad deg_to_rad(angle_deg) points.append(center Vector2(size * cos(angle_rad), size * sin(angle_rad))) draw_colored_polygon(points, color)3.3 寻路调用func find_path(start_q: int, start_r: int, end_q: int, end_r: int): var start_id get_hex_id(start_q, start_r) var end_id get_hex_id(end_q, end_r) if astar.has_point(start_id) and astar.has_point(end_id): var point_path astar.get_point_path(start_id, end_id) return point_path return []4. 高级应用技巧4.1 动态地形修改实时更新地形权重应对游戏中的地形变化func update_terrain(q: int, r: int, new_terrain: TerrainType): var point_id get_hex_id(q, r) if astar.has_point(point_id): terrain_map[point_id] new_terrain astar.set_point_weight_scale(point_id, TERRAIN_WEIGHTS[new_terrain])4.2 移动力限制模拟单位移动力Movement Points系统func find_path_with_mp(start_q: int, start_r: int, mp: float): var start_id get_hex_id(start_q, start_r) var reachable {} var queue [[start_id, 0.0]] while queue.size() 0: var current queue.pop_front() var current_id current[0] var current_cost current[1] if current_id in reachable: continue reachable[current_id] current_cost for neighbor_id in astar.get_point_connections(current_id): var edge_cost astar.get_point_weight_scale(neighbor_id) if current_cost edge_cost mp: queue.append([neighbor_id, current_cost edge_cost]) return reachable.keys()4.3 多单位避让使用临时障碍点实现单位间的路径避让var occupied_hexes {} func reserve_path(unit, path): for point in path: var hex_id get_hex_id_from_position(point) occupied_hexes[hex_id] unit func clear_path(unit): for hex_id in occupied_hexes.keys(): if occupied_hexes[hex_id] unit: occupied_hexes.erase(hex_id)5. 性能优化策略5.1 分块加载大型地图采用动态加载机制const CHUNK_SIZE 5 # 每块5x5六边形 var loaded_chunks {} func load_chunk(chunk_q: int, chunk_r: int): var chunk_key Vector2i(chunk_q, chunk_r) if chunk_key in loaded_chunks: return # 加载新区块 for q in range(chunk_q * CHUNK_SIZE, (chunk_q 1) * CHUNK_SIZE): for r in range(chunk_r * CHUNK_SIZE, (chunk_r 1) * CHUNK_SIZE): add_terrain_point(astar, q, r, generate_terrain(q, r)) loaded_chunks[chunk_key] true5.2 路径缓存对常用路径进行缓存优化var path_cache {} func get_cached_path(start_id: int, end_id: int): var cache_key str(start_id) _ str(end_id) if cache_key in path_cache: return path_cache[cache_key] var path astar.get_point_path(start_id, end_id) path_cache[cache_key] path return path5.3 分层寻路实现战略层和战术层的分层寻路var strategic_astar AStar2D.new() # 大地图粗略寻路 var tactical_astar AStar2D.new() # 小区域精确寻路 func init_strategic_layer(): # 每10x10六边形作为一个战略节点 for q in range(0, map_width, 10): for r in range(0, map_height, 10): strategic_astar.add_point(get_strategic_id(q, r), axial_to_pixel(q, r, CELL_SIZE * 10))

相关新闻