写得已经是很通俗了,可是其中
// if its already in the open list
else
{
// test if using the current G score make the aSquare F score lower, if yes update the parent because it means its a better path
}
这一部分,文章中并没有讲解为什么要这样做,我又重新找了几篇,也是都没有讲这一块,后来我看到这一篇:A*寻路算法初探
才搞明白,更新父节点是为了最后反向从目的地指向原点的时候能够找到最近的路径。而一般情况下,如果所有道路的通过代价都是一样的话是不会需要更新的(我猜测),只有从不同方向到达这个点所花代价不一样才可能出现需要更新。
明白了这点之后,其他的都比较简单,很好懂。
算法如下:
1,把StartNode添加到OpenList。
2,重复如下的工作:
a) 取出OpenList中F值最低的Node(可以把OpenList中的Node按顺序排列,F值最低的排在最前面)
b) 把它添加到CloseList中。
c) 对相邻的每一个NeighborNode进行判断
* 如果它不可通过或者已经在CloseList中,跳过。
* 如果它不在OpenList中,把它添加进去。把当前格作为这一格的父节点。记录这一格的F,G,和H值。
* 如果它已经在OpenList中,用G值为参考检查新的路径是否更好。更低的G值意味着更好的路径。如果是这样,就把这原有Node的父节点改成当前格,并且重新计算这一格的G和F值。如果你保持你的开启列表按F值排序,改变之后你可能需要重新对开启列表排序。
d) 停止,当你
* 把目标格添加进了CloseList,这时候路径被找到,或者
* 没有找到目标格,OpenList已经空了。这时候,路径不存在。
3.保存路径。从目标格开始,沿着每一格的父节点返回到起始格。这就是你的路径。
代码如下:
void mapGame::calBestPath()
{
auto startNode=new myNode(mySprite->getRowNum(), mySprite->getColNum());
startNode->setHValue(m_destRow, m_destCol);
m_openList.push_back(startNode);
myNode* bestNode;
int count1=0;
while (!m_openList.empty())
{
count1++;
//get most least F value Node
bestNode=*( m_openList.begin());
m_openList.pop_front();
m_closeList.push_back(bestNode);
//find dest position
if(bestNode->row==m_destRow&& bestNode->col==m_destCol) break;
//check neighbor node
generateNeighborNode(bestNode, bestNode->row+1, bestNode->col);
generateNeighborNode(bestNode,bestNode->row-1, bestNode->col);
generateNeighborNode(bestNode,bestNode->row, bestNode->col+1);
generateNeighborNode(bestNode,bestNode->row, bestNode->col-1);
}
CCLOG("count1:%d",count1);
//反向形成路径
myNode* nodeTmp=bestNode;
while(bestNode=bestNode->parent)
{
bestNode->next=node1;
node1=bestNode;
startNode=nodeTmp;
}
}
其中:
void mapGame::generateNeighborNode(myNode* bestNode,int row, int col)
{
if(row<0||row>=MAX_ROW||col<0||col>=MAX_COL) return;
//in closeList
auto index=getIndex(row,col);
if(isInList(m_closeList, index) )return ;
auto neighborNode=new myNode(row, col);
neighborNode->parent=bestNode;
neighborNode->setHValue(m_destRow, m_destCol);
neighborNode->GValue=bestNode->GValue+1;
auto oldNode=isInList(m_openList, getIndex(row, col));
if(oldNode)
{
//if already in openList, update parent if have smaller F value
if(oldNode->GValue > neighborNode->GValue)
{
oldNode->parent=bestNode;
delete neighborNode;
//此处要更新Node在Openlis中的位置!待修改
}
}
else
{
//if not in openList, add to OpenList
addToOpenList(neighborNode);
}
}
myNode* mapGame::isInList(std::list<myNode*> myList, int index)
{
for(auto nodeInList:myList)
{
if(getIndex(nodeInList->row, nodeInList->col)==index)
return nodeInList;
}
return NULL;
}
void mapGame::addToOpenList(myNode* addNode)
{
auto it=m_openList.begin();
int count2=0;
for(;it!=m_openList.end();it++)
{
if(addNode->getFValue()<=(*it)->getFValue()) break;//如果用小于号的话不能保证相等F值时新加的排在前面
count2++;
}
CCLOG("count2:%d",count2);
m_openList.insert(it, addNode);
}
节点定义:
class myNode
{
public:
int col;
int row;
int GValue;
int HValue;
myNode* parent;
myNode* next;
public:
myNode(int x, int y):col(y), row(x), parent(NULL), next(NULL),GValue(0), HValue(0){}
int getFValue(){return GValue+HValue;}
void setHValue(int destRow, int DestCol){ HValue=abs(destRow-row)+abs(DestCol-col);}
};
注意上面代码中的两个count,是我用来查看算法效率的。
关于count2,一开始给OpenList排序的时候我用的小于号,结果每次count2都好大,后来才发现是因为用小于号的话,F值与原有的相同的新加进来的节点并不会排在前面优先被选择,所以走了一些多余的路(当然最终通过原路返回后得到的路径是一样的)。
count1的次数与H值的计算方式有关,之前忘记取绝对值,结果简单几步的移动都要计算上百次,但是也能找到正确的路径,加了绝对值后效率高了很多。