系列文章目录`
第一章 A星寻路之大话西游2经典版-没地图的洞内自动寻路
前言
游戏一般都是有场景地图的。但是游戏场景却不提供地图,此时找寻任务坐标就没办法点击小地图后,自动寻路过去。下面,
以大话西游2经典版为例。
一、A星是什么?
A星是一个有效的求解最佳路径的搜索算法,一般应用于导航软件的最短路径计算、游戏寻路计算等。
A星的核心思想就是基于当前坐标,计算周边坐标是否可以移动,然后以周边可移动的坐标,分别作为下一个计算节点,继续计算节点
周围的坐标。如此往复循环,直至获取到终点坐标。然后根据终点坐标,回推出来要走的路径。
A星算法的核心公式为:F = G + H。G为起点到当前节点的实际代价(距离),H(本文也称曼哈顿距离,使用该函数作为计算)为当
前节点到终点的最短代价(无论下个坐标是否可以通过,都要视为可通过)。F的作用在下面计算过程中将会讲解。
了解了A星的基本用途和核心,下面我们就来图文配合具体讲解下A星的计算过程。
二、计算过程
1.准备工作
(白色为可通过区域,黑色为不可通过区域)
1、首先,通过观察上图可知,地图其实就是一个二维数组,我们以左下角为0,0开始分析过程,图中部分坐标标记了该坐标在数组中真实位置信息。(每个游戏的地图0,0点可能不一样,有的在左上,有的在左下,也有可能有反人类的在右边,在此我们以大话西游2经典版坐标体系为例,左下是0,0)
2、确定好地图以后,我们需要准备2个集合,一个叫做开放列表(集合list,以下称为Klist),一个叫做闭合列表(集合list,以下称为Blist)。Klist我们用来存放周边所有满足可以通过的节点,Blist我们用来存放Klist中F最小的那个值(一般来说,集合中存放的是个对象,该对象包含了该点在二维数组中的坐标信息、G值、H值、方向四种信息,不存放F是因为F=G+H)。
2.计算过程
1、我们获取0,0点作为起点,把它放到Klist中,然后从Klist中,取出F最小的那个值,也就是0,0点
2、拿到0,0点以后,开始获取他四周可以通过的区域,我们获取到了0,1(上方)和1,0(右侧)两个点。这两个点的F值都为7,此时我们要把{“0,1”,1,6,up}和{“1,0”,1,6,right}这两个节点的对象放到Klist中(存放顺时到Klist中,以及取最小值的顺序没有要求,在此,我们顺时针存值、倒序取值),然后吧0,0这个点放到Blist中(因为第一步从Klist中取F最小的值是0,0)。
3、我们再从Klist中取F值最小的那个对象(倒序取),发现最小的F是7,是0,1这个点。接下来我们要对0,1这个节点周边进行分析
1、0,1周围的节点只有up的0,2,right的1,1是黑色无法通过,所以我们不做记录。0,2节点对象信息"0,2",2,5,up存放到Klist中
2、我们倒序取F最小的那个,这次我们取出了1,0节点。并把0,1存入到了Blist中。
按照这个流程,我们最后会得到一条绿色填充的通到终点的结果集,也就是我们的Blist
2.路径回溯
通过上图,我们发现到达终点后,我们会得到一个存放了所有绿色坐标信息的Blist,此时,我们就要根据Blist中,每个对象的信息(up,left,down、right),来回溯路径,以确认真实有效的路径。
我们倒序Blist,"1,6"节点的方向信息是left,那么我们就要Blist集合中的"1+1,6"这个节点的信息,找到后。我们发现"1+1,6"节点的方向信息是left,那么我们就要在Blist中找"2+1,6"这个节点的信息,直到我们推到起点。
总结
以上就是今天要讲的内容,本文简单介绍了A星的使用。次方法的四方我们也可以扩展到8方,也就是斜对角的方格也可以计算在内,在此我们就不过多的介绍了。
代码我贴在最后:
function Aastart(mapName, ezhHeight, realMaxX, realMaxY, startX, startY, endX, endY) {
initAstart();
for (let a = 1; a <= ezhHeight; a++) {
aroundList = [];
if (mapName == "莲花洞") {
data = file.readLine("/sdcard/Download/lianhuadong.txt", a);
} else if (mapName == "火云洞") {
data = file.readLine("/sdcard/Download/huoyundong.txt", a);
} else if (mapName == "金兜洞") {
data = file.readLine("/sdcard/Download/jindoudong.txt", a);
} else if (mapName == "波月洞") {
data = file.readLine("/sdcard/Download/boyuedong.txt", a);
}
for (let b = 0; b < data.length; b++) {
aroundList.push(data[b])
}
map.push(aroundList);
}
startPoint = [Math.round(map[0].length / realMaxX * startX), map.length - 1 - Math.round(map.length / realMaxY * startY), 0, 0, ""]
finishPoint = [Math.round(map[0].length / realMaxX * endX), map.length - 1 - Math.round(map.length / realMaxY * endY), 0, 0, ""]
logd("X" + finishPoint[0] + "Y" + finishPoint[1]);
if (map[finishPoint[1]][finishPoint[0]] === 1) {
logd("finishPoint是障碍=" + 1);
}
openList.push(startPoint);
logd("startPoint" + startPoint + "finishPoint" + finishPoint);
while (true) {
//logd("=============================================================================================");
if (openList.length == 0 && !cmpNode(closeList, finishPoint)) {
is_lu = false
break
} else {
if (cmpNode(closeList, finishPoint)) {
jieguo = findPath(closeList, realMaxX, realMaxY);
is_lu = true
break
} else {
let minF = findMinNode(openList);
findNode(minF);
let index = openList.indexOf(minF);
openList.splice(index, 1);
if (!cmpNode(closeList, minF)) {
closeList.push(minF);
}
}
}
}
}
function findMinNode(arr) {
let minIndex = 0;
let minF = null;
let F = null;
let nodeMem = "";
if (arr != undefined) {
for (let a = 0; a < arr.length; a++) {
nodeMem = arr[a].toString().split(",");
F = parseInt(nodeMem[2]) + parseInt(nodeMem[3])
if (minF == null || F <= minF) {
minF = F;
minIndex = a;
}
}
}
return arr[minIndex];
}
function findNode(minF) {
let X = map[0].length - 1;
let Y = map.length - 1;
let minFX = -1;
let minFY = -1;
if (minF != undefined) {
minFX = parseInt(minF.toString().split(",")[0]);
minFY = parseInt(minF.toString().split(",")[1]);
}
let G = parseInt(minF.toString().split(",")[2])
if ((minFY - 1) >= 0 && map[minFY - 1][minFX] == 0) {
H = Math.abs(minFX - parseInt(finishPoint[0])) + Math.abs(minFY - 1 - parseInt(finishPoint[1]));
node = [minFX, minFY - 1, G + 1, H, "up"];
if (!cmpNode(closeList, node) && !cmpNode(openList, node)) {
openList.push(node);
}
}
//右
if ((minFX + 1) <= X && map[minFY][minFX + 1] == 0) {
H = Math.abs(minFX + 1 - parseInt(finishPoint[0])) + Math.abs(minFY - parseInt(finishPoint[1]));
node = [minFX + 1, minFY, G + 1, H, "right"];
if (!cmpNode(closeList, node) && !cmpNode(openList, node)) {
openList.push(node);
}
}
//下
if ((minFY + 1) <= Y && map[minFY + 1][minFX] == 0) {
H = Math.abs(minFX - parseInt(finishPoint[0])) + Math.abs(minFY + 1 - parseInt(finishPoint[1]));
node = [minFX, minFY + 1, G + 1, H, "down"];
if (!cmpNode(closeList, node) && !cmpNode(openList, node)) {
openList.push(node);
}
}
//左 114 38 109,72,22,39,left
if ((minFX - 1) >= 0 && map[minFY][minFX - 1] == 0) {
H = Math.abs(minFX - 1 - parseInt(finishPoint[0])) + Math.abs(minFY - parseInt(finishPoint[1]));
node = [minFX - 1, minFY, G + 1, H, "left"];
if (!cmpNode(closeList, node) && !cmpNode(openList, node)) {
openList.push(node);
}
}
}