热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

自动寻路之A星算法+Qt开发图形界面

文章目录项目演示A星算法理论Qt中代码讲解创建地图获取地图中任意点的坐标(终点坐标获取)通过键盘控制人物移动,并获取人物当前坐标ÿ


文章目录

    • 项目演示
    • A星算法理论
    • Qt中代码讲解
      • 创建地图
      • 获取地图中任意点的坐标(终点坐标获取)
      • 通过键盘控制人物移动,并获取人物当前坐标(起点坐标获取)
      • A*核心算法代码
      • 通过选取终点,利用QTimer实现自动寻路功能
      • Qt界面美化,封装完成


项目演示

最近笔者学习了一个新的知识,因为是车辆行业的研究生,对无人车、嵌入式物联网、机器人很感兴趣,所以在科研之余学习了一下A星算法,用时三个星期的周末,写了一个基于A星算法的游戏图形界面demo,其效果如图:
在这里插入图片描述


A星算法理论

无人车的路径规划问题涉及到A星算法、卡尔曼滤波算法、SLAM建图仿真等技术,其中A星算法应用到不仅是车辆的路径规划上,其在游戏行业有着很广泛的应用,比如lol中,我们想要控制自己的人物到地图上的某一点去,那么我们只需点击目的地,则人物会自动的按照算法算出来的最优路径进行自动导航到达我们指定的位置。在本项目中,没有向游戏中的地图那么复杂,笔者建立了简单的网格地图,来简单模拟游戏中的路径规划。
在这里插入图片描述
如上图中起点终点所示,黑色为障碍物,最优路径则为黄色区域。其算法核心即为启发式搜索,通过公式F=G+H得出:其中F叫做路径代价,G则为起点到当前点已经付出的代价累加,H为当前点到终点的预计代价。在这里我们假设每个方格为10×10的大小,那么G分为直线代价(10)和斜线代价(14),H为当前点到终点的预计代价并且无视障碍,那么像图中所示起点的下一步的代价总和已经可以计算得出,就这样每一步都执行8次代价计算(有障碍物则舍弃),选择代价总和最小的走,直到终点。


Qt中代码讲解


创建地图

地图的创建很简单,用一个二维数组即可,首先定义行数和列数,在这里我将定义一个枚举变量,令FLOOR为0,WALL为1。

int map[ROWS][COLS]={{WALL,WALL,WALL,WALL,WALL,WALL,WALL,WALL,WALL,WALL,WALL,WALL},{WALL,PERSON,FLOOR,FLOOR,FLOOR,WALL,FLOOR,FLOOR,FLOOR,FLOOR,FLOOR,WALL},{WALL,FLOOR,FLOOR,FLOOR,FLOOR,WALL,FLOOR,FLOOR,FLOOR,FLOOR,FLOOR,WALL},{WALL,FLOOR,FLOOR,FLOOR,WALL,WALL,FLOOR,FLOOR,WALL,FLOOR,FLOOR,WALL},{WALL,FLOOR,FLOOR,WALL,FLOOR,WALL,FLOOR,WALL,FLOOR,FLOOR,FLOOR,WALL},{WALL,FLOOR,FLOOR,WALL,FLOOR,WALL,FLOOR,WALL,FLOOR,FLOOR,FLOOR,WALL},{WALL,FLOOR,FLOOR,WALL,FLOOR,WALL,FLOOR,WALL,FLOOR,FLOOR,FLOOR,WALL},{WALL,FLOOR,FLOOR,FLOOR,FLOOR,FLOOR,FLOOR,WALL,FLOOR,FLOOR,FLOOR,WALL},{WALL,WALL,WALL,WALL,WALL,WALL,WALL,WALL,WALL,WALL,WALL,WALL},
};

获取地图中任意点的坐标(终点坐标获取)

因为我们的目的地是自己设置的,所以此项目中我通过鼠标点击地图的位置获取坐标作为我们设置的目的地:

if((p_x<(pt_x&#43;SIZE/2))&&(p_x>(pt_x-SIZE/2))&&(p_y>(pt_y-SIZE/2))&&(p_y<(pt_y&#43;SIZE/2))){str &#61; QString("%1 , %2").arg(pt[i][j].x()).arg(pt[i][j].y());p_end.row&#61;i;p_end.col&#61;j;/*设置显示在label中坐标的颜色*/QPalette pa;pa.setColor(QPalette::WindowText,Qt::red);ui->label->setPalette(pa);ui->label->setText(str);//显示}}}

因为地图是采取网格式的&#xff0c;所以在程序中进行了判断&#xff0c;当鼠标点击到某个网格中任意一点时&#xff0c;则将目的地坐标设置为此网格的中心坐标。


通过键盘控制人物移动&#xff0c;并获取人物当前坐标&#xff08;起点坐标获取&#xff09;

笔者在地图里通过WSAD键可以控制人物的上下左右移动&#xff0c;并且每次移动时将此时人物所在的坐标赋给起点。label中可以显示此时人物的坐标。

/*上下左右控制过程*/
void MainWindow::gameControl(enum Direction dir)
{//QPoint p &#61; QCursor::pos();//获取鼠标的绝对位置QString str;if (dir &#61;&#61; UP) { // 上方向if (map[man.y - 1][man.x] &#61;&#61; FLOOR ) { // 上方向为地板changeMap(man.y, man.x, FLOOR); // 原位置换为地板changeMap(man.y - 1, man.x, PERSON); // 上方向位置换为人str &#61; QString("%1 , %2").arg(pt[man.y-1][man.x].x()).arg(pt[man.y-1][man.x].y());p_start.row&#61;man.x-1;p_start.col&#61;man.y;ui->label->setText(str);//显示}}else if(dir &#61;&#61; DOWN){if (map[man.y &#43; 1][man.x] &#61;&#61; FLOOR ) { // 下方向为地板changeMap(man.y, man.x, FLOOR); // 原位置换为地板changeMap(man.y &#43; 1, man.x, PERSON); // 上方向位置换为人str &#61; QString("%1 , %2").arg(pt[man.y&#43;1][man.x].x()).arg(pt[man.y&#43;1][man.x].y());p_start.row&#61;man.x&#43;1;p_start.col&#61;man.y;ui->label->setText(str);//显示}}else if(dir &#61;&#61; LEFT){if (map[man.y][man.x-1] &#61;&#61; FLOOR ) { // 左方向为地板changeMap(man.y, man.x, FLOOR); // 原位置换为地板changeMap(man.y , man.x-1, PERSON); // 左方向位置换为人str &#61; QString("%1 , %2").arg(pt[man.y][man.x-1].x()).arg(pt[man.y][man.x-1].y());p_start.row&#61;man.x;p_start.col&#61;man.y-1;ui->label->setText(str);//显示}}else if(dir &#61;&#61; RIGHT){if (map[man.y ][man.x&#43;1] &#61;&#61; FLOOR ) { // 右方向为地板changeMap(man.y, man.x, FLOOR); // 原位置换为地板changeMap(man.y , man.x&#43;1, PERSON); // 右方向位置换为人str &#61; QString("%1 , %2").arg(pt[man.y][man.x&#43;1].x()).arg(pt[man.y][man.x&#43;1].y());p_start.row&#61;man.x;p_start.col&#61;man.y&#43;1;ui->label->setText(str);//显示}}
}

A*核心算法代码

代码的核心部分采用N叉树结构&#xff0c;即先将起点作为树的根&#xff0c;其次分出8个树枝&#xff0c;分别对应下一步的8个坐标方向&#xff0c;在这8个中将有障碍物的剔除&#xff0c;其余比较代价最小的那一个点&#xff0c;然后再将这个点作为树根&#xff0c;再依次向下伸张&#xff0c;直到达到终点。最后再依次获得之前所走路径的坐标。程序中用到了C&#43;&#43;中STL中的vector容器。


通过选取终点&#xff0c;利用QTimer实现自动寻路功能

此时算法核心已经完成。接下来实现图形化界面&#xff1a;
通过鼠标事件获取到终点后&#xff0c;此时路径上的坐标已经获得&#xff0c;则设置一个定时器&#xff0c;改变人物的位置

void MainWindow::start()
{if(p_end.col&#61;&#61;0&&p_end.row&#61;&#61;0){}else{timer &#61;new QTimer(this);timer->start(1000);connect(timer,&QTimer::timeout,this,&MainWindow::road);}
}

每次经过1秒&#xff0c;进行road函数&#xff0c;人物坐标变换一次

void MainWindow::road()
{if(j>0){changeMap(p_road[j-1].row,p_road[j-1].col,PERSON);changeMap(p_road[j].row,p_road[j].col,FLOOR);j--;}elsetimer->stop();
}

Qt界面美化&#xff0c;封装完成

在界面中设置了一些按钮的自定义&#xff0c;添加了界面背景的背景图&#xff0c;地图的资源图等

/******************************设置窗口格式**********************************///设置窗口格式this->setWindowFlag(Qt::FramelessWindowHint);//设置阴影QGraphicsDropShadowEffect *shadow &#61;new QGraphicsDropShadowEffect(this);ui->centralwidget->setGraphicsEffect(shadow);shadow-> setBlurRadius(10);shadow-> setColor(Qt::black);shadow-> setOffset(0);//父窗口设置透明this->setAttribute(Qt::WA_TranslucentBackground);/**************************************************************************/

到此项目基本完成&#xff0c;如果有需要交流和源代码的小伙伴可以加下我的vx&#xff1a;17362997119&#xff0c;希望能跟大佬们多交流交流&#xff01;&#xff01;


推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 本文介绍了pack布局管理器在Perl/Tk中的使用方法及注意事项。通过调用pack()方法,可以控制部件在显示窗口中的位置和大小。同时,本文还提到了在使用pack布局管理器时,应注意将部件分组以便在水平和垂直方向上进行堆放。此外,还介绍了使用Frame部件或Toplevel部件来组织部件在窗口内的方法。最后,本文强调了在使用pack布局管理器时,应避免在中间切换到grid布局管理器,以免造成混乱。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • Android获取app应用程序大小的方法
    Android获取app应用程序大小的方法-Android对这种方法进行了封装,我们没有权限去调用这个方法,所以我们只能通过AIDL,然后利用Java的反射机制去调用系统级的方法。 ... [详细]
  • 学习笔记17:Opencv处理调整图片亮度和对比度
    一、理论基础在数学中我们学过线性理论,在图像亮度和对比度调节中同样适用,看下面这个公式:在图像像素中其中:参数f(x)表示源图像像素。参数g(x)表示输出图像像素。 ... [详细]
  • Flutter 布局(四) Baseline、FractionallySizedBox、IntrinsicHeight、IntrinsicWidth详解
    本文主要介绍Flutter布局中的Baseline、FractionallySizedBox、IntrinsicHeight、IntrinsicWidth四种控件,详细介绍了其布局 ... [详细]
  • python+selenium十:基于原生selenium的二次封装fromseleniumimportwebdriverfromselenium.webdriv ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 如何优化Webpack打包后的代码分割
    本文介绍了如何通过优化Webpack的代码分割来减小打包后的文件大小。主要包括拆分业务逻辑代码和引入第三方包的代码、配置Webpack插件、异步代码的处理、代码分割重命名、配置vendors和cacheGroups等方面的内容。通过合理配置和优化,可以有效减小打包后的文件大小,提高应用的加载速度。 ... [详细]
  • Window10+anaconda+python3.5.4+ tensorflow1.5+ keras(GPU版本)安装教程 ... [详细]
author-avatar
mobiledu2502929547
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有