自动寻路之A星算法+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+SIZE/2))&&(p_x>(pt_x-SIZE/2))&&(p_y>(pt_y-SIZE/2))&&(p_y<(pt_y+SIZE/2)))
            {
                str = QString("%1 , %2").arg(pt[i][j].x()).arg(pt[i][j].y());
                  p_end.row=i;
                  p_end.col=j;
                  

                  /*设置显示在label中坐标的颜色*/
                  QPalette pa;
                  pa.setColor(QPalette::WindowText,Qt::red);
                  ui->label->setPalette(pa);

                ui->label->setText(str);//显示
            }

        }

        }

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

通过键盘控制人物移动,并获取人物当前坐标(起点坐标获取)

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

/*上下左右控制过程*/
void MainWindow::gameControl(enum Direction dir)
{

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

A*核心算法代码

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

通过选取终点,利用QTimer实现自动寻路功能

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

void MainWindow::start()
{
    if(p_end.col==0&&p_end.row==0)
    {

    }
    else
    {
     timer =new QTimer(this);
        timer->start(1000);
        connect(timer,&QTimer::timeout,this,&MainWindow::road);
    }
}

每次经过1秒,进行road函数,人物坐标变换一次

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--;
     }
     else
         timer->stop();
}

Qt界面美化,封装完成

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

/******************************设置窗口格式**********************************/
    //设置窗口格式
    this->setWindowFlag(Qt::FramelessWindowHint);
    //设置阴影
    QGraphicsDropShadowEffect *shadow =new QGraphicsDropShadowEffect(this);

    ui->centralwidget->setGraphicsEffect(shadow);
    shadow-> setBlurRadius(10);
    shadow-> setColor(Qt::black);
    shadow-> setOffset(0);
    //父窗口设置透明
    this->setAttribute(Qt::WA_TranslucentBackground);
    /**************************************************************************/

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

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码

)">
< <上一篇

)">
下一篇>>