深度寻路算法应用场景
仅用于空旷地形,小游戏或者大游戏的某个小模块,点击地图,人物一步步试探
广度寻路算法应用场景
只适用于小地图,回合制的走格子游戏,上帝视角,在走之前已经把路都找出来了
A星寻路算法应用场景
应用场景广泛
RPG游戏:为什么小兵追着人物砍?怎么样知道人物在哪?怎么样给它自动规划路径?人物自动跑图,怎么实现?
通过A星算法实现
以A星为基础的寻路算法:遗传算法,蚁群算法. . .
A星寻路算法排除了广度的劣势:循环比较多,每一个点都要涉及到,集合了深度的思想实现的-> 通过代价的概念来量化每走一步的消耗 f = g + h + w
A星会把一些无关紧要的分支不纳入考虑,会计算哪一种选择最佳-> 采用广度的方式,如果8 个点都能走,每个点都会纳入考虑,然后无限延伸
广度寻路算法只能走直线,A星寻路算法允许走斜线
A星寻路算法的核心是量化,选择最佳路径,应该选择代价低的(最近的)
f :最终做选择的依据、代价-> 用来评价寻路的快慢
g:从起点到当前点已经付出的代价-> 沉没成本
h:当前点到终点的预估代价
w:权重
这里举一个简单的例子:
沉没成本 g:小明和小红去创业,小明无工作 0 收入,小红工作每月收入2 W,小明的沉没成本就是 0元 / 月,小红的沉没成本就是 2W / 月,如果去创业,小红就相当于每个月损失2W,小明没有损失
预估代价 h:假设小明和小红创业的收益分别为:小明1.5W /月,小红3W / 月
最终整合起来:小明去创业更合适,他的整体代价最小
权重 w:创业过程中间可能存在一些问题
A星寻路算法也是每一步付出最小的代价达到结果,整体一路到终点,自然得到最佳路径
这里举一个简单的例子:
从广州到北京的代价?
飞机 高铁 火车 摩托 走路
钱 2k 1k 300 . . . . . .
时间 2h 5h 48h . . . . . .
需要设置预先代价,并且设置时要满足基本法:直角三角形三条边的长度需要满足勾股定理
蓝色部分为 g 值 红色部分为 h 值 紫色部分为最佳路径
准备一棵树:记录整个过程中的路线、经过
准备一个数组:纳入考虑的点要找出来哪一个 f 值最小
注意存在死胡同的情况
从起点(树的根节点)开始,每一步都是去找它周围能走的点,然后入树,放到容器中,找完之后,从容器中挑出来 f 值最小的那一个,把它从容器中删掉,让它变成树的当前点,再循环,从当前点开始,找当前点周围能走的点,入树,一路去找. . .
#include
#include
using namespace std;
//直线代价
#define ZXDJ 10
//斜线代价
#define XXDJ 14
//行数
#define ROWS 10
//列数
#define COLS 10
//枚举 0 ~ 7 8个方向
enum direct{p_up,p_down,p_left,p_right,p_lup,p_ldown,p_rup,p_rdown};
//描述一个点
struct MyPoint{
//private:
//xy坐标
int row;
int col;
//需要比较的值
int f;
int g;
int h;
public:
int getRow()const{ return row; }
int getCol()const{ return col; }
void setH(int h){
this->h = h;
}
//计算f值
void getF(){ f = g + h; }
//构造函数
MyPoint(){ row = col = f = g = h = 0; }
//有参构造函数
MyPoint(int y, int x){
row = y; col = x;
f = g = h = 0;
}
friend bool operator==(const MyPoint& p1, const MyPoint& p2);
friend ostream& operator<<(ostream& o, const MyPoint& p);
};
ostream& operator<<(ostream& o, const MyPoint& p){
return cout << "(" << p.row << "," << p.col << ")";
}
bool operator==(const MyPoint& p1, const MyPoint& p2){
if ((p1.row == p2.row) && (p1.col == p2.col)) return true;
return false;
}
//树的节点类型
struct treeNode{
MyPoint pos; //点-> 在地图中的下标
treeNode* pParent; //指向父节点的指针
vector child; //指向孩子节点的指针-> 有多少个孩子节点就有多少个指针
treeNode(){ pParent = NULL; } //构建一棵树
treeNode(const MyPoint& p){ this->pos = p; pParent = NULL; }
//void getH(const MyPoint& p){ }
};
//判断pos点能不能走,传入两个地图和点 能走返回true 否则返回false
bool canWalk(int map[ROWS][COLS], bool pathMap[ROWS][COLS], const MyPoint& pos);
//计算出pos的h值并返回
int getH(const MyPoint& pos, const MyPoint& endPos);
int main(){
//1 地图
int map[ROWS][COLS] = {
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }
};
//辅助地图用于标记已经走过-> 0表示false 表示没有走过
bool pathMap[ROWS][COLS] = { 0 };
//2 起点 终点
MyPoint begPos = { 1, 1 };
MyPoint endPos = { 7, 8 };
//3 准备一颗树
treeNode* pRoot = NULL;
//起点入树成为树的根节点
pRoot = new treeNode(begPos);
//标记起点走过
pathMap[begPos.getRow()][begPos.getCol()] = true;
//4 准备一个数组用来存储所有需要找 最小f的节点指针
vector buff;
vector::iterator it; //用来遍历
vector::iterator itMin; //保存f最小的元素地址
//5 寻路
treeNode* pCurrent = pRoot; //当前位置-> 树根 从树根开始赋值
treeNode* pChild = NULL; //当前点
bool isFindEnd = false;
while (!isFindEnd){
//5.1 找到周围能走的点-> 有8个点
for (int i = 0; i < 8; i++){
pChild = new treeNode;
pChild->pos = pCurrent->pos;//新的点和当前点位置相同
switch (i){
case p_up: pChild->pos.row--; pChild->pos.g += ZXDJ; break;
case p_down: pChild->pos.row++; pChild->pos.g += ZXDJ; break;
case p_left: pChild->pos.col--; pChild->pos.g += ZXDJ; break;
case p_right: pChild->pos.col++; pChild->pos.g += ZXDJ; break;
case p_lup: pChild->pos.row--; pChild->pos.col--; pChild->pos.g += XXDJ; break;
case p_ldown: pChild->pos.row++; pChild->pos.col--; pChild->pos.g += XXDJ; break;
case p_rup: pChild->pos.row--; pChild->pos.col++; pChild->pos.g += XXDJ; break;
case p_rdown: pChild->pos.row++; pChild->pos.col++; pChild->pos.g += XXDJ; break;
}//end of switch
//判断点能不能走
if (canWalk(map,pathMap,pChild->pos)){//能走
pChild->pos.h = getH(pChild->pos, endPos);//先算出h-> 当前点跟终点的直线距离 无视障碍
pChild->pos.getF();//再算出f
//入树-> 所有满足条件的点都入树
pCurrent->child.push_back(pChild); //如果能走 在树中pChild一定是pCurrent的孩子
pChild->pParent = pCurrent; //pChild的父指针指向当前点
//入buff
buff.push_back(pChild);
}
else{//不能走-> 释放
delete pChild;
pChild = NULL;
}
}//end of for
//5.2 找出buff中f最小的点-> 以当前点为中心的8个点都放在buff中了-> 遍历一个数组从里面找最大或者最小的
itMin = buff.begin();//假设第一个最小
for (it = buff.begin(); it != buff.end(); it++){//遍历
itMin = ( ( (*itMin)->pos.f<(*it)->pos.f ) ? itMin : it ); //根据f值大小赋值-> 最终itMin存储的是最小的
}
//5.3 pCurrent指向buff中f最小的点
pCurrent = *itMin;
//5.4 把最小的点从buff中删掉-> 否则每次都是这个点最小-> 先指向再删除 删除某个元素迭代器就失效了
buff.erase(itMin);
//5.5 判断是否找到终点 或者 永远找不到终点
if (pCurrent->pos == endPos){//找到终点
isFindEnd = true;
}
if (0 == buff.size()) break;//永远找不到终点
}//end of while
//6 显示路径
if (isFindEnd){
cout << "找到终点了:";
while (pCurrent){
cout << pCurrent->pos << " ";
pCurrent = pCurrent->pParent; //往前移动
}
cout << endl;
}
while (1);
return 0;
}
//判断pos点能不能走,能走返回true 否则返回false
bool canWalk(int map[ROWS][COLS], bool pathMap[ROWS][COLS], const MyPoint& pos){
//越界-> 超出数组范围
if (pos.row < 0 || pos.col < 0 || pos.row >= ROWS || pos.col >= COLS) return false;
//是障碍物
if (map[pos.row][pos.col]) return false;
//走过
if (pathMap[pos.row][pos.col]) return false;
//最终返回真
return true;
}
//计算出pos的h值并返回-> 传入当前点和终点
int getH(const MyPoint& pos, const MyPoint& endPos){
int x = ((endPos.col > pos.col) ? (endPos.col - pos.col) : (pos.col - endPos.col)); //大的减小的
int y = ((endPos.row > pos.row) ? (endPos.row - pos.row) : (pos.row - endPos.row)); //大的减小的
return ZXDJ*(x + y);
}
/*输出*/
找到终点了:(7,8) (7,7) (7,6) (7,5) (8,4) (7,3) (6,2) (5,2) (4,2) (3,2) (2,2) (1,1)