您现在的位置是:首页 >技术杂谈 >A*寻路之旅:用SDL图形化演示网站首页技术杂谈

A*寻路之旅:用SDL图形化演示

热爱编程的小K 2024-07-19 06:01:05
简介A*寻路之旅:用SDL图形化演示

前言

欢迎来到小K数据结构专栏的第十小节,本节将为大家带来A*寻路算法的图形化详解,学了之后寻路不再迷路(✨当然也为大家准备了完整的源码 )~希望你看完之后,能对你有所帮助,不足请指正!共同学习交流 ?

效果如下:

A*寻路算法图形化演示



一、简单介绍

由来
在 A * 算法之前有一种基于启发式探索的方法来提高Dijkstra算法的速度,这个算法叫做A1。后来的改进算法被称为A * 。 * 这个符号是从统计文献中借鉴来的,用来表示相对一个旧有标准的最优估计

启发式探索是利用问题拥有的启发信息来引导搜索,达到减少探索范围,降低问题复杂度的目的

✨A*寻路算法就是启发式探索的一个典型实践,在寻路的过程中,给每个节点绑定了一个估计值(即启发式),在对节点的遍历过程中是采取估计值优先原则,估计值更优的节点会被优先遍历。所以估计函数的定义十分重要,显著影响算法效率。
在这里插入图片描述

那么在上图中我们应该怎么评估出最短路径呐既然要评估,那肯定要有评估规则了,首先明确三个概念,H值,目前点到终点的曼哈顿距离(曼哈顿距离,就是两个位置长度差值和高度差值的和),G值,目前点到起点的消耗代价值,如果只是寻找路径,可以将该值也看成是这两点的曼哈顿距离,F值,H值和G值的和。所以A*寻路算法的评估由公示F=G+H来评估

✨我们先来尝试一下,假设每个格子的直线代价为10,斜线代价为14,则我们评估的起点周围的八个点的代价如下图所示:

在这里插入图片描述

怎么算的呐?我们以(2,2)为例,它到终点的曼哈顿距离我用黄色的矩形框起来了,横4纵4,然后乘上直线代价10,所以H为80,G一眼就可以看出,只有一个斜线代价,所以F为94

二、主要思想

在简单了解了A * 寻路算法了,我们不由得想,该怎么来寻?该用什么数据结构来描述?

  • ✨该怎么来寻?这个问题其实上边已经给出答案了,用F=G+H来评估,在这之前我们需要一个点类型,H比较好求,我们计算出当前点和终点之间的横纵坐标差,然后相加,乘上直线代价就好了,G值呐?我们通过下面的八叉树类型来解决~

    typedef struct Mypos 
    {
    	int row, col;
    	int f, g, h;
    }Mypos;
    //计算H值
    int getH(Mypos* pos, Mypos* endPos)
    {
        int x = ((pos->row > endPos->row) ? (pos->row - endPos->row) : (endPos->row - pos->row));
        int y = ((pos->col > endPos->col) ? (pos->col - endPos->col) : (endPos->col - pos->col));
        return (x + y) * ZXDJ;
    }
    
  • ✨该用什么数据结构?我首先想到的是八叉树,因为每个点周围都有八个点需要试探,下面是一个八叉树类型

    typedef struct MythreeNode 
    {
    	Mypos pos;                                     //点
    	struct MythreeNode* child[CHILD_NUM];          //孩子节点
    	struct MythreeNode* partent;                   //父节点
    	int child_Num;                                  //当前孩子数量
    }MythreeNode;
    
  • ✨具体的寻路过程如下

在这里插入图片描述

第一步,先遍历周围的八个节点,把他们的斜线代价计算出来

第二步,判断能不能走,能走就计算出F,存入树和数组中,不能走直接把该孩子删掉

第三步,从buffer数组中找到最小的F值,走,然后用辅助地图标记走过

第四步,我们要判断找没找到终点,退出循环有两种情况,要么是找到终点了,要么是buffer数组为空了

上述步骤中有一个小问题,就是如果遇到死胡同问题怎么办?比如下图:

在这里插入图片描述

第一步直接走到黄色的圈圈了,发现没路了,怎么办?我们思路回退一下,如果我们走完buffer数组中最小的,再把最小的删了不就可以了,这样下一步就会回到起点,这个问题就解决了

三、附上源码

✨A.h

#ifndef _A_H_
#define _A_H_
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<string.h>
#include<assert.h>
#include<SDL.h>
//行列
#define ROWS 10
#define COLS 10
//代价
#define ZXDJ 10
#define XXDJ 14
//最大孩子数量
#define CHILD_NUM 8
//临时数组容量
#define NUMS_SIZE 1024
//路
enum type { road, wall };
//方向
enum Mydirect { p_up, p_down, p_left, p_right, p_upleft, p_upright, p_downleft, p_downright };
//点类型
typedef struct Mypos 
{
	int row, col;
	int f, g, h;
}Mypos;
//八叉树类型
typedef struct MythreeNode 
{
	Mypos pos;                                     //点
	struct MythreeNode* child[CHILD_NUM];          //孩子节点
	struct MythreeNode* partent;                   //父节点
	int child_Num;                                  //当前孩子数量
}MythreeNode;
//获得H值
int getH(Mypos* pos, Mypos* endPos);
//创建八叉树节点
MythreeNode* create_ThreeNode(Mypos* pos);
//判断能不能走
bool Can_Walk(Mypos* pos, bool map[ROWS][COLS], bool Pathmap[ROWS][COLS]);
//加载图片
SDL_Texture* load_BMP(SDL_Renderer* Ren, const char* fillname);
//绘图
void draw_Map(bool map[ROWS][COLS], Mypos* pos, SDL_Renderer* Ren, SDL_Texture** tex);

#endif // _A_H_


✨A.c

#include "A.h"

int getH(Mypos* pos, Mypos* endPos)
{
    int x = ((pos->row > endPos->row) ? (pos->row - endPos->row) : (endPos->row - pos->row));
    int y = ((pos->col > endPos->col) ? (pos->col - endPos->col) : (endPos->col - pos->col));
    return (x + y) * ZXDJ;
}

MythreeNode* create_ThreeNode(Mypos* pos)
{
    MythreeNode* newNode = (MythreeNode*)malloc(sizeof(MythreeNode));
    if (NULL == newNode) return newNode;
    memset(newNode, 0, sizeof(MythreeNode));
    newNode->pos.row = pos->row;
    newNode->pos.col = pos->col;
    newNode->pos.g = pos->g;
    return newNode;
}

bool Can_Walk(Mypos* pos, bool map[ROWS][COLS], bool Pathmap[ROWS][COLS])
{
    //越界
    if (pos->row < 0 || pos->row >= ROWS || pos->col < 0 || pos->col >= ROWS) return false;
    //是墙
    if (map[pos->row][pos->col]) return false;
    //走过
    if (Pathmap[pos->row][pos->col]) return false;
    return true;
}

SDL_Texture* load_BMP(SDL_Renderer* Ren, const char* fillname)
{
    SDL_Surface* sfc = SDL_LoadBMP(fillname);
    if (!sfc)
    {
        SDL_Log("sfc filed %s", SDL_GetError());
        return NULL;
    }
    SDL_Texture* tex = SDL_CreateTextureFromSurface(Ren, sfc);
    if (!tex)
    {
        SDL_Log("tex failed %s", SDL_GetError());
        SDL_FreeSurface(sfc);
        return NULL;
    }
    SDL_FreeSurface(sfc);
    return tex;
}

void draw_Map(bool map[ROWS][COLS], Mypos* pos,SDL_Renderer* Ren,SDL_Texture** tex)
{
    for (int i = 0; i < ROWS; i++)
    {
        for (int j = 0; j < COLS; j++) 
        {
            SDL_Rect rect = { j * 64,i * 64,64,64 };

            if (!map[i][j])
            {
                SDL_RenderCopy(Ren, tex[2], NULL, &rect);
            }
            else if (map[i][j])
            {
                SDL_RenderCopy(Ren, tex[3], NULL, &rect);
            }
            if (pos->row == i && pos->col == j) 
            {
                SDL_RenderCopy(Ren, tex[0], NULL, &rect);
            }
           if (7 == i && 6 == j)
           {
               SDL_RenderCopy(Ren, tex[1], NULL, &rect);
           }

        }
    }
}

✨main.c

在这里插入图片描述

四、总结

本节讲解的数据结构——A*寻路算法,他不仅是一种算法思想,它还是路径规划,游戏中普通人物挂机状态的寻路的灵魂,所以它是值得我们花费时间去掌握的~下节见!

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。