目录
- 1.动画演示
- 2.游戏中的自动寻路A*算法
- 3.A*寻路算法原理
- 4.调试代码分析代码
- 5.代码
1.动画演示
2.游戏中的自动寻路A*算法
随着3D游戏的日趋流行,在复杂的3D游戏环境中如何能使非玩家控制角色准确实现自动寻路功能成为了3D游戏开
发技术中一大研究热点。其中A算法得到了大量的运用,A算法较之传统的路径规划算法;实时性更高、灵活性更强,寻路
结果更加接近人工选择的路径结果.A*寻路算法并不是找到最优路径,只是找到相对近的路径,因为找最优要把所有可行
路径都找出来进行对比,消耗性能太大,寻路效果只要相对近路径就行了。
3.A*寻路算法原理
寻路算法有深度优先搜索算法和广度优先搜索算法,不断根据自定义方向按照规定的方向不断的扫描,当扫描到终点后才能返回路径,但是有个更高效的寻路算法A*
A*算法主要是根据G、H、F三个重要的东西查找最好的路径
G:从起点到达当前已经走到的路径的距离长度
H:当前位置距离终点有多远(一般采用欧几里得求解两点距离,求当前位置的点到终点的距离就是H值)
F = G + H
图解如下:
准备两个表
openList和closeList
openList:待定选择的点,可能的路径的点
closeList:确定路径的点
伪算法:
1.开始起始位置,存放在openList中
while( openList != NULL) {
1.从openList获取最小F点坐标
2.将该点从openList删除
3.将该点加入到closeList中
4.查找该点周围满足条件的点
满足条件: 1. 没有超过地图边界
2. 不是墙壁
3. 不是已经走过的路径
for(遍历满足条件的点) {
if(判断这些点是否在openList中) {
1.计算G、H、F
2.添加到openList中
}
}
if(判断终点是否在closeList中)
返回终点
}
最终根据终点去查找前面一个点,前面一个点去查找它前面一个点,直到到达起点
4.调试代码分析代码
代码调试 起点坐标(6,8) 终点坐标(13,10)
接着返回最小的F值,由于是起点所以就是起点坐标 (6,8)
接着根据当前起点的坐标查找周围的格子,前提是满足条件的格子
这些周围的格子满足条件:
- 没有超过地图边界
- 不是墙壁
- 不是已经走过的路径
此时起点的周围点四个点都可以通过
通过函数搜索返回四个点的坐标如下
接着for遍历周围的点,判断这些点是否在开区间内,如果不在,就计算该点的G,H,F值
G值就是起始点到当前点的路径,一格表示10
H值就是当前点到终点的欧几里得两点距离公式计算根号(x2-x1)的平方 – (y2-y1)的平方得到两点距离就是H值,接着F = G + H得到90
接着将该点存入到openList中去
其他3个点也这样计算,最终openList表里面存储了起点的周围四个点,并且这些点都计算了G,H,F值
计算完后,选择最小的F的点作为需要走的路径的点
所以第一次选择坐标7,8的点,后面的走法跟这个一样
比如第一次从起始点开始选择,起始点上下左右四个方向,计算好G,H,F值后存入openList中,最终openlist存放了点(5,8)(7,8)(6,7)(6,9),第二次从openList待定点去查找F最小的是哪一个点,接着将该点作为起始点,查找该点四个方向同时计算该点的上下左右四个方向的点的GFH值,然后计算好后继续存入到openList中,等待第三次循环查找F最小的是哪一个点。然后将F最小的点存入closeList中,每次循环结束后,需要判断终点的坐标是否在closeList中,如果不在就继续找,直到最终终点存入到closelist中就结束,return closelist
本次程序采用链表,当起始点查找到了最小的F的点后,该点的指针域存储起始点
寻路步骤
1.从起点开始,把它作为待处理的方格存入一个预测可达的节点列表,简称openList,即把起点放入"预测可达节点列表”,可达节点列表openList就是一个等待检查方格的列表。
2.寻找openList中F值最小的点min (一开始只有起点)周围可以到达的方格(可到达的意思是其不是障碍物,也不存在关闭列表中的方格,即不是已走过的方格)。计算min周围可到达的方格的F值。将还没在openList 中点放入其中,并设置它们的"父方格"为点min,表示他们的上一步是经过min到达的。如果min下一步某个可到达的方格已经在openList列表那么并且经min点它的F值更优,则修改F值并把其"父方格"设为点min,
3.把2中的点min从"开启列表"中删除并存入"关闭列表"closeList 中, closeList中存放的都是不需要再次检查的方格。如果2中点min 不是终点并且开启列表的数量大于零,那么继续从第2步开始。如果是终点执行第4步,如果openList列表数量为零,那么就是找不到有效路径。
4.如果第3步中min是终点,则结束查找,直接追溯父节点到起点的路径即为所选路径。
5.代码
main.cpp
#include <iostream>
#include <graphics.h>
#include "Astar.h"
using namespace std;
int width = lines * 80;
int height = cols * 40;
int maze[18][20] =
{ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0 },
{ 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0 },
{ 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0 },
{ 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0 },
{ 0, 2, 2, 2, 2, 0, 0, 2, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 0 },
{ 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 0 },
{ 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 0 },
{ 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 0 },
{ 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 0 },
{ 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0 },
{ 0, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 0, 2, 0, 2, 2, 0 },
{ 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 2, 0 },
{ 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
int cols = 20;
int lines = 18;
IMAGE img[4];
void init() {
loadimage(&img[0], "res//wall2.jpg", 80, 40);
loadimage(&img[1], "res//start.png", 80, 40);
loadimage(&img[2], "res//end.png", 80, 40);
loadimage(&img[3], "res//空地.png", 80, 40);
}
int Count = 0;
void draw() {
auto msg = GetMouseMsg();
char s[12];
char s1[12];
if (Count != 2) {
if (msg.uMsg == WM_LBUTTONDOWN)
{
int row = msg.y / 40;
int col = msg.x / 80;
if (Count == 1) {
maze[col][row] = 3;
Count++;
}
if (Count == 0) {
maze[col][row] = 1;
Count++;
}
}
}
for (int i = 0; i < lines; i++) {
for (int j = 0; j < cols; j++) {
if (maze[i][j] == 1) {
putimage(i * 80, j * 40, &img[1]);
}
else if (maze[i][j] == 3) {
putimage(i * 80, j * 40, &img[2]);
}
if (maze[i][j] == 2) {
putimage(i * 80, j * 40, &img[0]);
}
if (maze[i][j] == 0) {
putimage(i * 80, j * 40, &img[3]);
}
}
}
}
vector<Point> FindPath() {
vector<Point> Path;
Point strat;
for (int i = 0; i < lines; i++) {
for (int j = 0; j < cols; j++) {
if (maze[i][j] == 1) {
strat.x = i;
strat.y = j;
Path.push_back(strat);
}
if (maze[i][j] == 3) {
strat.x = i;
strat.y = j;
Path.push_back(strat);
}
}
}
return Path;
}
void clear(vector<Point> path) {
vector<Point>::iterator it;
for (it = path.begin(); it != path.end();) {
maze[(*it).x][(*it).y] = 0;
it = path.erase(it);
}
}
void clear_draw(vector<Point>& set, list<Point*>& path) {
Count = 0;
path.clear();
clear(set);
set.clear();
}
int main() {
init();
initgraph(width, height);
BeginBatchDraw();
while (1) {
cleardevice();
vector<Point> set;
if (Count == 2)
set = FindPath();
Point p;
if (Count == 2) {
auto path = GetPath(&set.front(), &set.back());
path.pop_back();
if (!path.empty()) {
for (list<Point*>::iterator it = path.begin(); it != path.end(); it++) {
maze[(*it)->x][(*it)->y] = 1;
p.x = (*it)->x;
p.y = (*it)->y;
set.push_back(p);
}
draw();
FlushBatchDraw();
system("pause");
clear_draw(set, path);
}
}
draw();
FlushBatchDraw();
}
return 0;
}
Astra.cpp
#pragma once
#include <math.h>
#include "Astar.h"
#include <iostream>
static std::list<Point*> openList;
static std::list<Point*> closeList;
static Point* findPath(Point& startPoint, Point& endPoint, bool isIgnoreCorner);
static std::vector<Point*> getSurroundPoints(const Point* point);
static bool isCanreach(const Point* point, const Point* target);
static Point* isInList(const std::list<Point*>& list, const Point* point);
static Point* getLeastFpoint();
static int calcG(Point* temp_start, Point* point);
static int calcH(Point* point, Point* end);
static int calcF(Point* point);
Point* AllocPoint(int x, int y) {
Point* temp = new Point;
memset(temp, 0, sizeof(Point));
temp->x = x;
temp->y = y;
return temp;
}
void InitAstarMaze( int _lines, int _colums)
{
lines = _lines;
cols = _colums;
}
void ClearAstarMaze() {
std::list<Point*>::iterator itor;
for (itor = openList.begin(); itor != openList.end();) {
itor = openList.erase(itor);
}
for (itor = closeList.begin(); itor != closeList.end();) {
itor = closeList.erase(itor);
}
}
static int calcG(Point* temp_start, Point* point)
{
int extraG = (abs(point->x - temp_start->x) + abs(point->y - temp_start->y)) == 1 ? kCost1 : kCost2;
int parentG = (point->parent == NULL ? NULL : point->parent->G);
return parentG + extraG;
}
static int calcH(Point* point, Point* end)
{
return (int)sqrt((double)(end->x - point->x) * (double)(end->x - point->x) + (double)(end->y - point->y) * (double)(end->y - point->y)) * kCost1;
}
static int calcF(Point* point)
{
return point->G + point->H;
}
static Point* getLeastFpoint()
{
if (!openList.empty())
{
auto resPoint = openList.front();
std::list<Point*>::const_iterator itor;
for (itor = openList.begin(); itor != openList.end(); itor++) {
if ((*itor)->F < resPoint->F)
resPoint = *itor;
}
return resPoint;
}
return NULL;
}
Point* findPath(Point* startPoint, Point* endPoint)
{
openList.push_back(AllocPoint(startPoint->x, startPoint->y));
while (!openList.empty())
{
auto curPoint = getLeastFpoint();
openList.remove(curPoint);
closeList.push_back(curPoint);
auto surroundPoints = getSurroundPoints(curPoint);
std::vector<Point*>::const_iterator iter;
for (iter = surroundPoints.begin(); iter != surroundPoints.end();)
{
Point* target = *iter;
Point* exist = isInList(openList, target);
if (!exist)
{
target->parent = curPoint;
target->G = calcG(curPoint, target);
target->H = calcH(target, endPoint);
target->F = calcF(target);
openList.push_back(target);
iter++;
}
else
{
iter = surroundPoints.erase(iter);
}
}
surroundPoints.clear();
Point* resPoint = isInList(closeList, endPoint);
if (resPoint) {
return resPoint;
}
}
return NULL;
}
std::list<Point*> GetPath(Point* startPoint, Point* endPoint)
{
Point* result = findPath(startPoint, endPoint);
std::list<Point*> path;
while (result)
{
path.push_front(result);
result = result->parent;
}
ClearAstarMaze();
return path;
}
static Point* isInList(const std::list<Point*>& list, const Point* point)
{
std::list<Point*>::const_iterator itor;
for (itor = list.begin(); itor != list.end(); itor++) {
if ((*itor)->x == point->x && (*itor)->y == point->y) {
return *itor;
}
}
return NULL;
}
bool isCanreach(const Point* point, const Point* target)
{
if (target->x<0 || target->x>(lines - 1)
|| target->y<0 || target->y>(cols - 1)
|| maze[target->x][target->y] == 2
|| (target->x == point->x && target->y == point->y)
|| isInList(closeList, target))
return false;
else
{
if (abs(point->x - target->x) + abs(point->y - target->y) == 1) {
return true;
}
else
{
return false;
}
}
}
std::vector<Point*> getSurroundPoints(const Point* point)
{
std::vector<Point*> surroundPoints;
for (int x = point->x - 1; x <= point->x + 1; x++) {
for (int y = point->y - 1; y <= point->y + 1; y++) {
Point* temp = AllocPoint(x, y);
if (isCanreach(point, temp)) {
surroundPoints.push_back(temp);
}
}
}
return surroundPoints;
}
Astar.h
#pragma once
#include <vector>
#include <list>
extern int maze[][20];
extern int cols;
extern int lines;
const int kCost1 = 10;
const int kCost2 = 14;
typedef struct _Point
{
int x, y;
int F, G, H;
struct _Point* parent;
}Point;
Point* AllocPoint(int x, int y);
void InitAstarMaze(int _lines, int _colums);
std::list<Point*> GetPath(Point* startPoint, Point* endPoint);
void ClearAstarMaze();
运行结果:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)