AI 游戏中的自动寻路-A*算法
随着 3D 游戏的日趋流行,在复杂的 3D 游戏环境中如何能使非玩家控制角色准确实现自动寻路功能成为了 3D 游戏开发技术中一大研究热点。其中 A算法得到了大量的运用,A算法较之传统的路径规划算法,实时性更高、灵活性更强,寻路结果更加接近人工选择的路径结果. A*寻路算法并不是找到最优路径,只是找到相对近的路径,因为找最优要把所有可行路径都找出来进行对比,消耗性能太大,寻路效果只要相对近路径就行了。
A* 算法的原理
我们假设在推箱子游戏中人要从站里的地方移动到右侧的箱子目的地,但是这两点之间被一堵墙隔开。
我们下一步要做的便是查找最短路径。既然是 AI 算法, A* 算法和人寻找路8径97的9做4法38十4分0类1似11,1当我们离目标较远时,我
们的目标方向是朝向目的点直线移动,但是在近距离上因为各种障碍需要绕行(走弯路)!而且已走过的地方就无须再次
尝试。
令 F = G + H ,F 即表示从起点经过此点预计到终点的总移动距离
接下来我们从起点开始,按照以下寻路步骤,直至找到目标。
寻路步骤
- 从起点开始, 把它作为待处理的方格存入一个预测可达的节点列表,简称 openList, 即把起点放入“预测可达节点列表”,
可达节点列表 openList 就是一个等待检查方格的列表。 - 寻找 openList 中 F 值最小的点 min(一开始只有起点)周围可以到达的方格(可到达的意思是其不是障碍物,也不存
在关闭列表中的方格,即不是已走过的方格)。计算 min 周围可到达的方格的 F 值。将还没在 openList 中点放入其中, 并
设置它们的"父方格"为点 min,表示他们的上一步是经过 min 到达的。如果 min 下一步某个可到达的方格已经在 openList
列表那么并且经 min 点它的 F 值更优,则修改 F 值并把其"父方格"设为点 min。 - 把 2 中的点 min 从"开启列表"中删除并存入"关闭列表"closeList 中, closeList 中存放的都是不需要再次检查的方格。如
果 2 中点 min 不是终点并且开启列表的数量大于零,那么继续从第 2 步开始。如果是终点执行第 4 步,如果 openList 列
表数量为零,那么就是找不到有效路径。
4.如果第 3 步中 min 是终点,则结束查找,直接追溯父节点到起点的路径即为所选路径。
#include<stdio.h>
#include<Windows.h>
#include<iostream>
#include<queue>
using namespace std;
#define MAX_SIZE 1024
typedef struct _EdgNode{//定义边
int adjvex; //邻接的顶点
int weight; //权重
_EdgNode *next;//下一条边
}EdgNode;
typedef struct _VertexNode{//顶点节点
char date;//节点名A,B,C
EdgNode *first;//邻接的第一条边
}VerTexNode,AdjList;
typedef struct _AdjListGraph{
AdjList *adjlist;
int vex;//顶点
int edge;//边数
}AdjListGraph;
//全局数组
bool visit[MAX_SIZE];
int Location(AdjListGraph &G,char v);
void DFS(AdjListGraph &G,char c);
//1、图的初始化
void init(AdjListGraph &G){
G.adjlist = new AdjList[MAX_SIZE];//初始化顶点的个数
G.edge = 0;//边
G.vex = 0;//顶点
for(int i=0;i<MAX_SIZE;i++){
visit[i] = false;//初始化都设为未被访问过的下一节点
}
}
//2、图的创建
void CreateDFS(AdjListGraph &G){
int weight=0;
//初始化顶点和边
cout<<"请输入创建的顶点个数和边的个数:"<<endl;
cin>>G.vex>>G.edge;
cout<<"请输入相应的顶点:"<<endl;
//先输入具体的顶点名字
for(int i=0;i<G.vex;i++){
cin>>G.adjlist[i].date;
G.adjlist[i].first = NULL;
}
char v1=0,v2=0;
int i1,i2;
cout<<"请输入相关联的相连接的顶点"<<endl;
for(int i=0;i<G.edge;i++){
//输入进v1,输入进v2
cin>>v1>>v2>>weight;//我们要找到顶点的具体下标
i1 = Location(G,v1);
i2 = Location(G,v2);
if(v1!=-1&&v2!=-1){
//来进行插入节点
EdgNode *Edg = new EdgNode;
Edg->adjvex = i2;
Edg->weight = weight;
Edg->next = G.adjlist[i1].first;
G.adjlist[i1].first = Edg;//
}
}
}
//3、图的Location
int Location(AdjListGraph &G,char c){
for(int i=0;i<G.vex;i++){
if(G.adjlist[i].date==c){
return i;//返回相应的下标
}
}
//否则
return -1;
}
//4、图的DFS
//作用是遍历节点
int minweights=0x7fffffff; //定义最小权重,初值给的是2^32-1
int steps = 0; //定义走过的步长
int Path[MAX_SIZE]={0}; //保存当前走的路径的每一个顶点date对应的下标
int shortPath[MAX_SIZE]={0}; //保存最小权值对应的下标
void DFS(AdjListGraph &G,int start,int end,int weights){
int cur =-1;
//当start = end 时说明已经找到终点
cur =start;
if(cur==end){
cout<<"路径\t\t";
for(int i=0;i<steps;i++){
cout<<G.adjlist[Path[i]].date<<" ";//打印的是当前路径
}
cout<<"长度为"<<weights;
cout<<endl;
if(minweights>weights){
minweights = weights;
memcpy(shortPath,Path,steps*sizeof(int));//给最路径存储下标
}
}
//当没有找到终点时将该店的adjust置为true
visit[start] = true;
EdgNode *temp = G.adjlist[start].first;//下一个顶点
//我们要不断的遍历所有的路径都要给他弄出来
while(temp){
int weight = temp->weight;//获取当前边的权值
cur = temp->adjvex;//获取当前边对应的下一顶点的date对应的下标
//当前顶点的下一个顶点存在继续向下进攻
if(visit[cur]==false){
//当然下一个顶点设置为已访问
visit[cur] = true;
Path[steps++]=cur;//只有当进来的时候才能算走了一步,存储cur
DFS(G,cur,end,weight+weights);//递归调用
visit[cur] = false;// 当前的节点置为false,方便接下来的遍历
Path[--steps] = 0;//步数减一从递归中出来了
}
temp = temp->next;//这么走一趟只能说明走了一条路径
}
}
//广度遍历
void main(){
AdjListGraph G;
init(G);
CreateDFS(G);
char c1,c2;
cout<<"请输入要查询的最短路径的起点和终点"<<endl;
cin>>c1>>c2;
DFS(G,Location(G,c1),Location(G,c2),0);
cout<<"最小的路径长度为"<<minweights<<endl;
int i=0;
while(i<MAX_SIZE&&shortPath[i]>0){
cout<<G.adjlist[shortPath[i]].date<<"";
i++;
}
cout<<endl;
system("pause");
}