""" 路径查找器 实现基于导航网格的寻路算法 """ import heapq import math from typing import List, Optional, Dict, Tuple from panda3d.core import Point3 class PathfinderNode: """ 寻路节点 用于A*算法中的节点表示 """ def __init__(self, polygon_id: int, position: Point3): self.polygon_id = polygon_id self.position = position self.g_cost = 0.0 # 从起点到当前节点的实际代价 self.h_cost = 0.0 # 启发式代价(到终点的估计代价) self.f_cost = 0.0 # 总代价 (g_cost + h_cost) self.parent = None # 父节点 def __lt__(self, other): """用于优先队列比较""" return self.f_cost < other.f_cost def __eq__(self, other): """用于节点比较""" return self.polygon_id == other.polygon_id class Pathfinder: """ 路径查找器 实现基于导航网格的A*寻路算法 """ def __init__(self, navmesh_manager): """ 初始化路径查找器 Args: navmesh_manager: 导航网格管理器 """ self.navmesh_manager = navmesh_manager # 寻路设置 self.heuristic_weight = 1.0 # 启发式权重 self.max_path_nodes = 10000 # 最大路径节点数 # 性能统计 self.stats = { 'paths_found': 0, 'paths_not_found': 0, 'total_search_time': 0.0, 'total_nodes_searched': 0 } print("✓ 路径查找器初始化完成") def find_path(self, start_point: Point3, end_point: Point3) -> Optional[List[Point3]]: """ 查找从起点到终点的路径 Args: start_point: 起点 end_point: 终点 Returns: 路径点列表,如果找不到路径则返回None """ try: # 检查导航网格是否可用 if not self.navmesh_manager or not self.navmesh_manager.polygons: print("⚠ 导航网格不可用") return None # 获取起点和终点所在的多边形 start_polygon = self.navmesh_manager.get_polygon_containing_point(start_point) end_polygon = self.navmesh_manager.get_polygon_containing_point(end_point) # 如果起点或终点不在导航网格上,使用最近的点 if not start_polygon: closest_start = self.navmesh_manager.get_closest_point(start_point) if not closest_start: print("⚠ 无法找到起点附近的可行走区域") return None start_point = closest_start start_polygon = self.navmesh_manager.get_polygon_containing_point(start_point) if not end_polygon: closest_end = self.navmesh_manager.get_closest_point(end_point) if not closest_end: print("⚠ 无法找到终点附近的可行走区域") return None end_point = closest_end end_polygon = self.navmesh_manager.get_polygon_containing_point(end_point) # 如果起点和终点在同一个多边形内,直接连接 if start_polygon.polygon_id == end_polygon.polygon_id: return [start_point, end_point] # 使用A*算法查找路径 path = self._a_star_search(start_polygon.polygon_id, end_polygon.polygon_id, start_point, end_point) if path: self.stats['paths_found'] += 1 print(f"✓ 找到路径,包含 {len(path)} 个节点") return path else: self.stats['paths_not_found'] += 1 print("⚠ 未找到路径") return None except Exception as e: print(f"✗ 路径查找失败: {e}") return None def _a_star_search(self, start_polygon_id: int, end_polygon_id: int, start_point: Point3, end_point: Point3) -> Optional[List[Point3]]: """ A*寻路算法实现 Args: start_polygon_id: 起始多边形ID end_polygon_id: 目标多边形ID start_point: 起点 end_point: 终点 Returns: 路径点列表,如果找不到路径则返回None """ import time start_time = time.time() # 开放列表(优先队列) open_list = [] # 关闭列表(已访问的节点) closed_list = set() # 创建起始节点 start_node = PathfinderNode(start_polygon_id, start_point) start_node.g_cost = 0 start_node.h_cost = self._calculate_heuristic(start_point, end_point) start_node.f_cost = start_node.g_cost + start_node.h_cost # 将起始节点加入开放列表 heapq.heappush(open_list, start_node) # 节点搜索计数 nodes_searched = 0 while open_list and nodes_searched < self.max_path_nodes: # 取出f_cost最小的节点 current_node = heapq.heappop(open_list) nodes_searched += 1 # 如果到达目标节点 if current_node.polygon_id == end_polygon_id: # 重构路径 path = self._reconstruct_path(current_node) # 添加终点 path.append(end_point) self.stats['total_search_time'] += time.time() - start_time self.stats['total_nodes_searched'] += nodes_searched return path # 将当前节点加入关闭列表 closed_list.add(current_node.polygon_id) # 获取当前多边形 if current_node.polygon_id not in self.navmesh_manager.polygons: continue current_polygon = self.navmesh_manager.polygons[current_node.polygon_id] # 检查所有相邻多边形 for neighbor_id in current_polygon.neighbors: # 如果已在关闭列表,跳过 if neighbor_id in closed_list: continue # 获取邻居多边形 if neighbor_id not in self.navmesh_manager.polygons: continue neighbor_polygon = self.navmesh_manager.polygons[neighbor_id] # 计算到邻居节点的代价 neighbor_position = neighbor_polygon.center tentative_g_cost = current_node.g_cost + \ self._calculate_distance(current_node.position, neighbor_position) # 检查是否需要更新邻居节点 existing_node = self._find_node_in_open_list(open_list, neighbor_id) if existing_node is None: # 创建新节点 neighbor_node = PathfinderNode(neighbor_id, neighbor_position) neighbor_node.g_cost = tentative_g_cost neighbor_node.h_cost = self._calculate_heuristic(neighbor_position, end_point) neighbor_node.f_cost = neighbor_node.g_cost + neighbor_node.h_cost neighbor_node.parent = current_node heapq.heappush(open_list, neighbor_node) elif tentative_g_cost < existing_node.g_cost: # 更新现有节点 existing_node.g_cost = tentative_g_cost existing_node.f_cost = existing_node.g_cost + existing_node.h_cost existing_node.parent = current_node # 未找到路径 self.stats['total_search_time'] += time.time() - start_time self.stats['total_nodes_searched'] += nodes_searched return None def _calculate_heuristic(self, point1: Point3, point2: Point3) -> float: """ 计算启发式代价(使用欧几里得距离) Args: point1: 起点 point2: 终点 Returns: 启发式代价 """ return (point2 - point1).length() * self.heuristic_weight def _calculate_distance(self, point1: Point3, point2: Point3) -> float: """ 计算两点间距离 Args: point1: 第一个点 point2: 第二个点 Returns: 两点间距离 """ return (point2 - point1).length() def _find_node_in_open_list(self, open_list: List[PathfinderNode], polygon_id: int) -> Optional[PathfinderNode]: """ 在开放列表中查找指定多边形ID的节点 Args: open_list: 开放列表 polygon_id: 多边形ID Returns: 找到的节点,如果未找到则返回None """ for node in open_list: if node.polygon_id == polygon_id: return node return None def _reconstruct_path(self, end_node: PathfinderNode) -> List[Point3]: """ 重构路径 Args: end_node: 终点节点 Returns: 路径点列表 """ path = [] current = end_node # 从终点回溯到起点 while current is not None: path.append(current.position) current = current.parent # 反转路径(从起点到终点) path.reverse() return path def smooth_path(self, path: List[Point3]) -> List[Point3]: """ 平滑路径(可选优化) Args: path: 原始路径 Returns: 平滑后的路径 """ if len(path) <= 2: return path smoothed_path = [path[0]] # 简单的路径平滑算法 i = 0 while i < len(path) - 1: # 尝试直接连接当前点和后面的点 j = len(path) - 1 while j > i: # 检查两点间是否可以直接通行 if self._is_line_walkable(path[i], path[j]): smoothed_path.append(path[j]) i = j break j -= 1 else: # 如果不能直接连接,添加下一个点 i += 1 if i < len(path): smoothed_path.append(path[i]) return smoothed_path def _is_line_walkable(self, start: Point3, end: Point3) -> bool: """ 检查两点间直线是否可行走 Args: start: 起点 end: 终点 Returns: 是否可行走 """ # 简化实现:检查线段上的几个点是否都在可行走区域 steps = 10 for i in range(steps + 1): t = i / steps point = start + (end - start) * t if not self.navmesh_manager.is_point_walkable(point): return False return True def get_pathfinding_stats(self) -> Dict: """ 获取寻路统计信息 Returns: 统计信息字典 """ return self.stats.copy() def reset_stats(self): """重置统计信息""" self.stats = { 'paths_found': 0, 'paths_not_found': 0, 'total_search_time': 0.0, 'total_nodes_searched': 0 } def set_heuristic_weight(self, weight: float): """ 设置启发式权重 Args: weight: 权重值 """ self.heuristic_weight = max(0.0, weight) def set_max_path_nodes(self, max_nodes: int): """ 设置最大路径节点数 Args: max_nodes: 最大节点数 """ self.max_path_nodes = max(1, max_nodes) def find_path_with_multiple_waypoints(self, waypoints: List[Point3]) -> Optional[List[Point3]]: """ 查找经过多个路点的路径 Args: waypoints: 路点列表 Returns: 完整路径,如果任何一段找不到则返回None """ if len(waypoints) < 2: return None # 查找每段路径并连接 full_path = [] for i in range(len(waypoints) - 1): segment_path = self.find_path(waypoints[i], waypoints[i + 1]) if not segment_path: return None # 如果任何一段找不到路径,则整个路径失败 # 避免重复点 if full_path: full_path.pop() # 移除上一段的终点(与这一段的起点重复) full_path.extend(segment_path) return full_path def find_random_path(self) -> Optional[List[Point3]]: """ 查找随机路径(用于测试) Returns: 随机路径 """ if not self.navmesh_manager: return None start_point = self.navmesh_manager.get_random_point() end_point = self.navmesh_manager.get_random_point() if start_point and end_point: return self.find_path(start_point, end_point) return None