AStar寻路自我总结(19.7.20)
学习完bather老师的寻宝,对其中涉及到择中选择路线的AStar算法深感迷惑。
核心思想为通过开启列表来确认周边是否还有有效方向,再通过设定好的endData来反推路径
- 起点(第一个当前)从开启中删除,然后加入当前列表。(虽然是起点,~~但是还是要设定起点的数据为“VISITED”~~此句勘误,其实是START )。
- 然后寻找八方向方块,并进行有效性校验
- 此时可以进行第一次判定,是否有一个方向的方块,是终点方块?是,退出:不是,继续
- 如果八方向中有不是空地或者为障碍物(“WALL”)的方格,就不再扩展,该方格pass
- 都通过校验后,开始更新g,h的值(如果八方向中有格子(B(n))已经在开启列表中了,则B(n)也会被忽视第5条后半句的操作,因为它要进行路线优化再检查),则加入开启列表,并将父节点设置为当前节点(nowData)的节点(是设置为,不是改为)。
- 此时会出现最绕的一个逻辑:假设此时经过很多次循环,开启列表中有不少元素,在每次选择完F值最小的格子(minData)后照常进行八方向判定,此时很有可能会重复扫到已经在开启列表中的格子(B(active))。此时就需要对这几个格子进行G值判断——即是minData的新一轮到B(active)的G值与它原来的G值比较。如果是新一轮的大,则无视这个B(active),反之则B(active)成为新路径,并且将B(active)的G更新,变成前者的值,并将parentPoint设为minData。
- 如果通过了其他路线优化再检查,则在开启列表中寻找F值最小的格子,设为当前列表中的元素,并从开启列表中删除。(循环)
- 最后一步就是做找到endData(“END”)后,反推路径的操作了
for (PointData pathData = endData; endData != null;)
{
Point point = pathData.point;
if (MAP[point.x,point.y]==VISITED)
{
MAP[point.x, point.y] = ON_PATH;
}
pathData = pathData.parent;
}
- 补充:因为A星寻路的核心逻辑在单独类中,这个类又不直接引用GameManager下的数据,所以采用自己设定字符的方法创建一个char[,]的二维数组来保存地图数据,后可以直接对接。
- 补充:这个类中有单独的对接GameManager地图数据的方法,并在初始化的时候设定权重(“SPACE0”,“SAPCE1”)。理解方法为:水往低处流。
private static double HManhattanDistance(Point pnt)
{
if (MAP[pnt.x, pnt.y] == SPACE0)
{
return Math.Abs(pnt.x - END_PNT.x) + Math.Abs(pnt.y - END_PNT.y);
}
else
{
return Math.Abs(pnt.x - END_PNT.x) + Math.Abs(pnt.y - END_PNT.y) * 10000;
}
}
....
public static bool GenerateMap(Point s, Point e){
for (int i = 0; i < MAX_PNT.x; i++)
{
for (int j = 0; j < MAX_PNT.y; j++)
{
if (GameManager.Instance.mapArray[i, j].elementsContent == ElementsContent.Door ||
GameManager.Instance.mapArray[i, j].elementsContent == ElementsContent.Enemy ||
GameManager.Instance.mapArray[i, j].elementsContent == ElementsContent.BigWall ||
GameManager.Instance.mapArray[i, j].elementsContent == ElementsContent.SmallWall ||
GameManager.Instance.mapArray[i, j].elementsState == ElementsState.Marked ||
(GameManager.Instance.mapArray[i, j].elementsContent == ElementsContent.Trap && GameManager.Instance.mapArray[i, j].elementsState == ElementsState.Uncovered)
)
{
MAP[i, j] = WALL;
}
else if (GameManager.Instance.mapArray[i, j].elementsContent == ElementsContent.Tool && ((ToolElement)GameManager.Instance.mapArray[i, j]).isHide == false||
GameManager.Instance.mapArray[i, j].elementsContent == ElementsContent.Gold && ((GoldElement)GameManager.Instance.mapArray[i, j]).isHide == false||
GameManager.Instance.mapArray[i, j].elementsState==ElementsState.Uncovered)
{
MAP[i, j] = SPACE0;
}
else
{
MAP[i, j] = SPACE1;
}
}
}
}
- 如此一来,角色就会优先走已经掀开的工具和已经掀开的金币以及已经翻开的泥块,而WALL则永远不会进入开启列表。