NAV导航网格寻路(6) -- 寻路实现
这篇是转的文章,原文 http://blianchen.blog.163.com/blog/static/13105629920103911258517/
前面已经介绍了寻路的方法,现在给出我的一个实现。
A*寻找网格路径
A*算法就不说了,网上很多,这里只说下三角形网格如何使用A*算法,如下图,绿色直线代表最终路径和方向,路径线进入三角形的边称为穿入边,路径线出去的边称为穿出边。每个三角形的花费(g值)采用穿入边和穿出边的中点的距离(图中红线),至于估价函数(h值)使用该三角形的中心点(3个顶点的平均值)到路径终点的x和y方向的距离。
下面只贴出关键代码
private var m_CellVector:Vector.<Cell>; private var openList:Heap; //二叉堆 private var closeList:Array; /** * 构建网格路径,该方法生成closeList * @param startCell 起始点所在网格 * @param startPos 起始点坐标 * @param endCell 终点所在网格 * @param endPos 终点坐标 * @return */ public function buildPath(startCell:Cell, startPos:Vector2f, endCell:Cell, endPos:Vector2f):void{ openList.clear(); closeList.length = 0; openList.put(endCell); endCell.f = 0; endCell.h = 0; endCell.isOpen = false; endCell.parent = null; endCell.sessionId = pathSessionId; var foundPath:Boolean = false; //是否找到路径 var currNode:Cell; //当前节点 var adjacentTmp:Cell = null; //当前节点的邻接三角型 while (openList.size > 0) { // 1. 把当前节点从开放列表删除, 加入到封闭列表 currNode = openList.pop(); closeList.push(currNode); //路径是在同一个三角形内 if (currNode == startCell) { foundPath = true; break; } // 2. 对当前节点相邻的每一个节点依次执行以下步骤: //所有邻接三角型 var adjacentId:int; for (var i:int=0; i<3; i++) { adjacentId = currNode.links[i]; // 3. 如果该相邻节点不可通行或者该相邻节点已经在封闭列表中, // 则什么操作也不执行,继续检验下一个节点; if (adjacentId < 0) { //不能通过 continue; } else { adjacentTmp = m_CellVector[adjacentId]; //m_CellVector 保存所有网格的数组 } if (adjacentTmp != null) { if (adjacentTmp.sessionId != pathSessionId) { // 4. 如果该相邻节点不在开放列表中,则将该节点添加到开放列表中, // 并将该相邻节点的父节点设为当前节点,同时保存该相邻节点的G和F值; adjacentTmp.sessionId = pathSessionId; adjacentTmp.parent = currNode; adjacentTmp.isOpen = true; //H和F值 adjacentTmp.computeHeuristic(startPos); //m_WallDistance 是保存三角形各边中点连线的距离,共3个 adjacentTmp.f = currNode.f + adjacentTmp.m_WallDistance[Math.abs(i - currNode.m_ArrivalWall)]; //放入开放列表并排序 openList.put(adjacentTmp); // remember the side this caller is entering from adjacentTmp.setAndGetArrivalWall(currNode.index); } else { // 5. 如果该相邻节点在开放列表中, // 则判断若经由当前节点到达该相邻节点的G值是否小于原来保存的G值, // 若小于,则将该相邻节点的父节点设为当前节点,并重新设置该相邻节点的G和F值 if (adjacentTmp.isOpen) {//已经在openList中 if (currNode.f + adjacentTmp.m_WallDistance[Math.abs(i - currNode.m_ArrivalWall)] < adjacentTmp.f) { adjacentTmp.f = currNode.f; adjacentTmp.parent = currNode; // remember the side this caller is entering from adjacentTmp.setAndGetArrivalWall(currNode.index); } } else {//已在closeList中 adjacentTmp = null; continue; } } } } } }
由close list取出网格路径
/** * 路径经过的网格 * @return */ private function getCellPath():Vector.<Cell> { var pth:Vector.<Cell> = new Vector.<Cell>(); var st:Cell = closeList[closeList.length-1]; pth.push(st); while (st.parent != null) { pth.push(st.parent); st = st.parent; } return pth; }
根据网格路径计算路径点
算法前面已经详细说明,以下是代码
/** * 根据经过的三角形返回路径点(下一个拐角点法) * @param start * @param end * @return Point数组 */ private function getPath(start:Vector2f, end:Vector2f):Array { //经过的三角形 var cellPath:Vector.<Cell> = getCellPath(); //没有路径 if (cellPath == null || cellPath.length == 0) { return null; } //保存最终的路径(Point数组) var pathArr:Array = new Array(); //开始点 pathArr.push(start.toPoint()); //起点与终点在同一三角形中 if (cellPath.length == 1) { pathArr.push(end.toPoint()); //结束点 return pathArr; } //获取路点 var wayPoint:WayPoint = new WayPoint(cellPath[0], start); while (!wayPoint.position.equals(end)) { wayPoint = this.getFurthestWayPoint(wayPoint, cellPath, end); pathArr.push(wayPoint.position); } return pathArr; } /** * 下一个拐点 * @param wayPoint 当前所在路点 * @param cellPath 网格路径 * @param end 终点 * @return */ private function getFurthestWayPoint(wayPoint:WayPoint, cellPath:Vector.<Cell>, end:Vector2f):WayPoint { var startPt:Vector2f = wayPoint.position; //当前所在路径点 var cell:Cell = wayPoint.cell; var lastCell:Cell = cell; var startIndex:int = cellPath.indexOf(cell); //开始路点所在的网格索引 var outSide:Line2D = cell.sides[cell.m_ArrivalWall]; //路径线在网格中的穿出边 var lastPtA:Vector2f = outSide.getPointA(); var lastPtB:Vector2f = outSide.getPointB(); var lastLineA:Line2D = new Line2D(startPt, lastPtA); var lastLineB:Line2D = new Line2D(startPt, lastPtB); var testPtA:Vector2f, testPtB:Vector2f; //要测试的点 for (var i:int=startIndex+1; i<cellPath.length; i++) { cell = cellPath[i]; outSide = cell.sides[cell.m_ArrivalWall]; if (i == cellPath.length-1) { testPtA = end; testPtB = end; } else { testPtA = outSide.getPointA(); testPtB = outSide.getPointB(); } if (!lastPtA.equals(testPtA)) { if (lastLineB.classifyPoint(testPtA, EPSILON) == PointClassification.RIGHT_SIDE) { //路点 return new WayPoint(lastCell, lastPtB); } else { if (lastLineA.classifyPoint(testPtA, EPSILON) != PointClassification.LEFT_SIDE) { lastPtA = testPtA; lastCell = cell; //重设直线 lastLineA.setPointB(lastPtA); } } } if (!lastPtB.equals(testPtB)) { if (lastLineA.classifyPoint(testPtB, EPSILON) == PointClassification.LEFT_SIDE) { //路径点 return new WayPoint(lastCell, lastPtA); } else { if (lastLineB.classifyPoint(testPtB, EPSILON) != PointClassification.RIGHT_SIDE) { lastPtB = testPtB; lastCell = cell; //重设直线 lastLineB.setPointB(lastPtB); } } } } return new WayPoint(cellPath[cellPath.length-1], end); //终点 }