和四边形地图的A*规则相同,主要是H值的计算,简化为等边三角形和平行四边形
1. 起始点放入open列表
// A*算法计算最短路径
// G是从开始点到达当前方块的移动量
// H值是从当前方块到终点的移动量估算值
// 重复以下步骤来找到最短路径:
// 将方块添加到open列表中,该列表有最小的和值F(F=G+H)。且将这个方块称为S吧。
// 将S从open列表移除,然后添加S到closed列表中。
// 对于与S相邻的每一块可通行的方块T:
// 如果T在closed列表中:不管它。
// 如果T不在open列表中:添加它然后计算出它的和值。
// 如果T已经在open列表中:当我们使用当前生成的路径到达那里时,检查F 和值是否更小。如果是,更新它的和值和它的前继。
// http://www.cnblogs.com/zhoug2020/p/3468167.html
getAStarShortestPath: function() {
this._AStarOpenSize = 0;
this._AStarOpenList = {};
this._AStarCloseList = {};
this._AStarPath = [];
this._boatLineList = [];
// 0. 起始点放入open列表
var HValue = this.calculateAStarHValueByPos(this._AStarStartPos);
this._AStarOpenList[this.X_LARGE*this._AStarStartPos.x + this._AStarStartPos.y] = {x:this._AStarStartPos.x, y:this._AStarStartPos.y, ex:this.B_LARGE, G:0, H:HValue, F:HValue, i:this._AStarOpenSize};
var astarResult = this.circleUpdateTheCloseList();
if (astarResult) {
for (var tagid in this._AStarCloseList) {
cc.log("the close list :"+JSON.stringify(this._AStarCloseList[tagid]));
}
// 从Close列表中通过终点逆推找出最短路径并排序
this._AStarPath.unshift(this._AStarEndPos);
var temp = this;
var sortShorestPath = null;
sortShorestPath = function() {
var ex = temp._AStarCloseList[temp.X_LARGE*temp._AStarPath[0].x + temp._AStarPath[0].y].ex;
if (ex == temp.B_LARGE) {
return;
}
temp._AStarPath.unshift({x:temp._AStarCloseList[ex].x, y:temp._AStarCloseList[ex].y});
sortShorestPath();
};
sortShorestPath();
}
},
2. 递归open列表取出最小F值放入close列表并取其临近坐标
// 递归计算最小和值更新open和close列表,直到终点在close列表中
circleUpdateTheCloseList: function() {
// 1. 对open列表中取最小和值F的坐标S进行操作
var minF = 100000000;
var minFPosTag = -1;
for (var tagid in this._AStarOpenList) {
// 最简单(快速)的方法是一直跟着最近被添加到open列表中的方块
if (this._AStarOpenList[tagid].F - 0.0001*this._AStarOpenList[tagid].i < minF) {
minF = this._AStarOpenList[tagid].F - 0.0001*this._AStarOpenList[tagid].i;
minFPosTag = tagid;
}
}
if (minFPosTag != -1) {
var minFPosData = this._AStarOpenList[minFPosTag];
delete this._AStarOpenList[minFPosTag];
this._AStarCloseList[minFPosTag] = minFPosData;
// final. 如果是终点,结束判断
if (minFPosData.x == this._AStarEndPos.x && minFPosData.y == this._AStarEndPos.y) {
cc.log("find the final pos");
return 1;
}
cc.log("find the minF pos ("+minFPosData.x+","+minFPosData.y+")");
// 2. 对S相邻点T进行操作,更新F,G,H的值(F=G+H)和father(ex)和插入顺序(i)
var exPos = minFPosData;
var nearList = this.getNeighborListByPos(exPos);
for (var i = 0; i < nearList.length; i++) {
var pos = nearList[i];
if (this._AStarCloseList[this.X_LARGE*pos.x + pos.y]) {
// ignore
} else {
var GValue = exPos.G + 1;
var openExist = this._AStarOpenList[this.X_LARGE*pos.x + pos.y];
if (openExist == null) this._AStarOpenSize += 1;
if ((openExist && openExist.G > GValue) || openExist == null) {
var HValue = this.calculateAStarHValueByPos(pos);
this._AStarOpenList[this.X_LARGE*pos.x + pos.y] = {x:pos.x, y:pos.y, ex:this.X_LARGE*exPos.x+exPos.y, G:GValue, H:HValue, F:HValue+GValue, i:this._AStarOpenSize};
cc.log((openExist ? "update" : "add")+" the open pos "+JSON.stringify(this._AStarOpenList[this.X_LARGE*pos.x + pos.y]));
}
}
}
return this.circleUpdateTheCloseList();
} else {
cc.log("not found cloest A* path");
return 0;
}
},
3. 六边形计算H值,通过简化等边三角形和平行四边形计算曼哈顿距离
// 计算坐标点距离目标点的移动量估算值H
calculateAStarHValueByPos: function(pos) {
var x = pos.x;
var y = pos.y;
var endx = this._AStarEndPos.x;
var endy = this._AStarEndPos.y;
var gapx = Math.abs(endx-x);
if (gapx%2 == 0) {
// 距离坐标点对应等边三角形的边上任意点距离相同,为y轴增量,剩下的距离为平行四边形计算
if (endy >= y-gapx/2 && endy <= y+gapx/2) {
return gapx;
} else if (endy < y-gapx/2) {
return gapx + Math.abs(endy-y+gapx/2);
} else if (endy > y+gapx/2) {
return gapx + Math.abs(endy-y-gapx/2);
}
} else {
// 寻找坐标点对应等边三角形,三角形另外两个坐标点x根据所在列进行计算,剩下的距离为平行四边形计算
if (Math.abs(x)%2 == 0) {
if (endy >= y-gapx/2+0.5 && endy <= y+gapx/2+0.5) {
return gapx;
} else if (endy < y-gapx/2+0.5) {
return gapx + Math.abs(endy-y+gapx/2-0.5);
} else if (endy > y+gapx/2+0.5) {
return gapx + Math.abs(endy-y-gapx/2-0.5);
}
} else {
if (endy >= y-gapx/2-0.5 && endy <= y+gapx/2-0.5) {
return gapx;
} else if (endy < y-gapx/2-0.5) {
return gapx + Math.abs(endy-y+gapx/2+0.5);
} else if (endy > y+gapx/2-0.5) {
return gapx + Math.abs(endy-y-gapx/2+0.5);
}
}
}
cc.log("A* H value calculate error");
},
4. 相邻坐标的计算根据坐标命名规则进行计算