寒假想用cocos creator做一个简单的小游戏,需要用到自动寻路,网上找了很久,目前最流行的使用A星(A*)算法实现,所以简单记录一下。如有错误,请指正。
目录
A*算法原理
网上A*算法原理有很多,这一篇写的很好,可以学习一下AStar
实现
理解A*算法的原理之后,开始着手进行实现,我用的引擎是cocos creator,所以使用Js语言完成。
地图的生成
Astar算法实现的原理就是根据点与点之间的距离,通过比较优先级来选择最佳路径。所以首先将地图和障碍物转化为容易区分的二维数组,选择二维数组的原因是:坐标可以用来计算,值可以反应该点是否为障碍物。为了使算法运行更快,将地图和障碍物的宽高各缩小10倍。
实现的代码:
getMapNavMesh(){
var width = (this.map.node.width); //地图的宽高
var height = (this.map.node.height);
var map = new Array();
for(i=0;i<96;i++){
map[i]=new Array();
for(j=0;j<64;j++){
map[i].push(0);
};
};
//更改将障碍物所在的值
for(i = 0;i<this.obstacles.length;i++){
var x = parseInt((this.obstacles[i].node.x)+(width/2));
var y = parseInt((this.obstacles[i].node.y)+(height/2));
var obsWidth=parseInt(this.obstacles[i].node.width);
var obsHeight =parseInt(this.obstacles[i].node.height);
realX=parseInt((x+obsWidth)/width*96);
realY=parseInt((y+obsHeight)/height*64);
console.log(x,y);
for(m=parseInt((x)/width*96);m<realX;m++){
for(n=parseInt((y)/height*64);n<realY;n++){
map[m][n]=1;
};
}
};
return map;
},
上述代码就是将地图上障碍物所在坐标的值变为1,没有障碍物的坐标值为0.
A*算法前的准备
A*算法主要是比较点之间的距离,所以自定义Point类
//点
Point(x, y) {
return {
x: x,
y: y,
eq: function (other) {
return this.x == other.x && this.y == other.y;
}
}
},
A*算法
AStar(map2d, startPoint, endPoint, passTag) {
var tag = passTag || 0;
self = this;
var Node = function (point, endPoint, g) {
//描述AStar中的节点
var tG = g || 0;
return {
point: point, //节点的坐标
father: null, //父节点
g: tG, //G值,g值在用到的时候会重新算
h: (Math.abs(endPoint.x - point.x) + Math.abs(endPoint.y - point.y)) * 10 //计算H值
}
};
return {
map2d: map2d,
startPoint: startPoint,
endPoint: endPoint,
passTag: tag,
openList: [], //开启表
closeList: [], //关闭表
//获得openList中F值最小的节点
getMinNode: function () {
var currentNode = this.openList[0];
for (var node of this.openList) {
if (node.g + node.h < currentNode.g + currentNode.h)
currentNode = node;
}
return currentNode;
},
//判断point是否在关闭表中
pointInCloseList: function (point) {
for (var node of this.closeList) {
if (node.point.eq(point))
return true;
}
return false;
},
//判断point是否在开启表中
pointInOpenList: function (point) {
for (var node of this.openList) {
if (node.point.eq(point))
return node;
}
return null;
},
//判断终点是否在关闭表中
endPointInCloseList: function () {
for (var node of this.closeList) {
if (node.point.eq(this.endPoint))
return node;
}
return null;
},
//搜索节点周围的点
searchNear: function (minF, offsetX, offsetY) {
//越界检测
if (minF.point.x + offsetX < 0 || minF.point.x + offsetX > this.map2d.w - 1 || minF.point.y + offsetY < 0 || minF.point.y + offsetY > this.map2d.h - 1)
return null;
//如果是障碍就忽略
if (this.map2d.data[minF.point.x + offsetX][minF.point.y + offsetY] !== this.passTag)
return null;
//如果在关闭表中就忽略
var currentPoint = self.Point(minF.point.x + offsetX, minF.point.y + offsetY);
if (this.pointInCloseList(currentPoint))
return null;
//设置单位花费
var step = 0;
if (offsetX === 0 || offsetY === 0)
step = 10;
else
step = 14;
//如果不在openList中,就把它加入openList
var currentNode = this.pointInOpenList(currentPoint);
if (currentNode == null) {
currentNode = Node(currentPoint, this.endPoint, minF.g + step);
currentNode.father = minF;
this.openList.push(currentNode);
return null;
}
//如果在openList中,判断minF到当前点的G是否更小
if (minF.g + step < currentNode.g) {
currentNode.g = minF + step;
currentNode.father = minF;
}
},
//开始寻路
start: function () {
//1.将起点放入开启列表
var startNode = Node(this.startPoint, this.endPoint);
this.openList.push(startNode);
//2.主循环逻辑
while (true) {
//找到F值最小的节点
var minF = this.getMinNode();
//把这个点加入closeList中,并且在openList中删除它
this.closeList.push(minF);
var index = this.openList.indexOf(minF);
this.openList.splice(index, 1);
//搜索这个节点的上下左右节点
this.searchNear(minF, 0, -1);
this.searchNear(minF, 0, 1);
this.searchNear(minF, -1, 0);
this.searchNear(minF, 1, 0);
// 判断是否终止
var point = this.endPointInCloseList();
if (point) {
//如果终点在关闭表中,就返回结果
var cPoint = point;
var pathList = [];
while (true) {
if (cPoint.father) {
pathList.push(cPoint.point);
cPoint = cPoint.father;
} else {
return pathList.reverse();
}
}
}
//开启表为空
if (this.openList.length === 0)
return null;
}
}
}
},
最终实现的效果: