A星寻路算法的学习总结(详解)

2023-05-16

目录

1.理论基础

1.1A星寻路是用来解决什么问题的

1.2A星寻路的基本原理

2.代码实现

2.1每个格子的信息

2.2A星寻路管理器

2.3测试代码

3.实例演示


1.理论基础

1.1A星寻路是用来解决什么问题的

A星寻路是用来计算玩家行进最短路径的

1.2A星寻路的基本原理

不停的找周围的点,通过找出寻路消耗最小的点作为新的起点再循环的找,直到找到终点。

寻路消耗公式

f(寻路消耗)=g(离起点距离)+h(离终点距离);

开启列表和关闭列表

每次寻找的时候把周围不是阻挡的点且不在开启列表或者关闭列表的点放在开启列表中,每次开启列表中寻路消耗最低的点放入关闭列表中,判断该点是否是终点,如果是则寻路结束,反之则继续寻路

死路

如果开启列表为空的时候,则判定为死路。

2.代码实现

2.1每个格子的信息

创建格子类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 格子类型
/// </summary>
public enum NodeType
{
    walk,//可以走

    stop,//不能走
}
public class AStarNode
{
    //格子对象坐标
    public int x;
    public int y;
    //寻路消耗
    public float f;
    //离起点的距离
    public float g;
    //离终点的距离
    public float h;
    //父对象
    public AStarNode father;

    public NodeType type;

    public AStarNode (int x,int y,NodeType type)
    {
        this.x = x;
        this.y = y;
        this.type = type;
    }

}

2.2A星寻路管理器

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AStarManager 
{
    private static AStarManager instance;
    public static AStarManager Instance
    {
        get
        {
            if (instance == null)
               instance = new AStarManager();
            return instance;
        }
    }
    //地图的宽高
    int mapW;
    int mapH;

    //地图相关所有的格子对象容器
    public  AStarNode[,] nodes;
    //开启列表和关闭列表
    private List<AStarNode> openList=new List<AStarNode>();
    private List<AStarNode> closeList=new List<AStarNode>() ;

    //初始化地图信息
    public  void InitMapInfo(int w,int h)
    {
        this.mapH = h;
        this.mapW = w;
        nodes = new AStarNode[w, h];
        for(int i=0;i<w;i++)
        {
            for(int j=0;j<h;j++)
            {
                //随机阻挡只是为了测试,真正的项目中阻挡信息应该是从地图的配置文件中读出来的
                AStarNode node = new AStarNode(i, j, Random.Range(0, 100) < 20 ? NodeType.stop: NodeType.walk);
                nodes[i, j] = node;
            }
        }
    }

    public  List<AStarNode> FindPath(Vector2 startPos,Vector2 endPos)
    {
        //实际项目中,传入的点往往是世界坐标系的位置,可能是小数,需要转化成整数也就是转化成每个格子的坐标,这里默认传入的数据为整数
        //首先判断传入的两个点是否合法
        //1.判断是否在地图内
        if (startPos.x < 0 || startPos.x >= mapW || startPos.y < 0 || startPos.y >= mapH || endPos.x < 0 || endPos.x >= mapW || endPos.y < 0 || endPos.y >= mapH)
        {
            Debug.Log("开始或者结束点在地图外");
                 return null;
        }
        //2.判断是否是阻挡
        AStarNode startNode = nodes[(int)startPos.x,(int)startPos.y];
        AStarNode endNode = nodes[(int)endPos.x, (int)endPos.y];
        if (startNode.type == NodeType.stop || endNode.type == NodeType.stop)
        {
            Debug.Log("开始或者结束点是阻挡");
            return null;
        }

        openList.Clear();
        closeList.Clear();

        startNode.father = null;
        startNode.h = 0;
        startNode.g = 0;
        startNode.f = 0;
        closeList.Add(startNode);

        while(true)
        {
            //寻找四周的点
            //左上
            FindNearlyNodetoOpenList(startNode.x - 1, startNode.y + 1, 1.4f, startNode, endNode);
            //上
            FindNearlyNodetoOpenList(startNode.x, startNode.y + 1, 1f, startNode, endNode);
            //右上
            FindNearlyNodetoOpenList(startNode.x + 1, startNode.y + 1, 1.4f, startNode, endNode);
            //左
            FindNearlyNodetoOpenList(startNode.x - 1, startNode.y, 1f, startNode, endNode);
            //右
            FindNearlyNodetoOpenList(startNode.x + 1, startNode.y, 1f, startNode, endNode);
            //左下
            FindNearlyNodetoOpenList(startNode.x - 1, startNode.y - 1, 1.4f, startNode, endNode);
            //下
            FindNearlyNodetoOpenList(startNode.x, startNode.y - 1, 1f, startNode, endNode);
            //右下
            FindNearlyNodetoOpenList(startNode.x + 1, startNode.y - 1, 1.4f, startNode, endNode);

            //死路判断
            if (openList.Count==0)
            {
                return null;
            }

            openList.Sort(SortOpenList);
            startNode = openList[0];

            closeList.Add(openList[0]);
            openList.RemoveAt(0);

            if(startNode==endNode)
            {
                List<AStarNode> path = new List<AStarNode>();
                path.Add(endNode);
                while(endNode.father!=null)
                {
                    path.Add(endNode.father);
                    endNode = endNode.father;
                }
                path.Reverse();
                return path;
            }
        }
    }
     //将最近的点放进开始列表并计算寻路消耗
    private void FindNearlyNodetoOpenList(int x,int y,float g,AStarNode father,AStarNode end)
    {
        //边界判断
        if (x < 0 || x > mapW || y < 0 || y > mapH)
            return;

        AStarNode node = nodes[x, y];
        //判断是否是阻挡
        if (node == null || node.type == NodeType.stop || closeList.Contains(node) || openList.Contains(node))
            return;

        node.father = father;
        node.g = father.g + g;
        node.h = Mathf.Abs(end.x - x) + Mathf.Abs(end.y - y);
        node.f = node.g + node.h;

        openList.Add(node);
    }
    //开始列表排序
    private int SortOpenList(AStarNode a, AStarNode b)
    {
        if (a.f > b.f)
            return 1;
        else if (a.f == b.f)
            return 1;
        else
            return -1;
    }
}

2.3测试代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestAStar : MonoBehaviour
{
    //左上角第一个立方体的位置
    public int beginX=-3;
    public int beginY=5;

    //之后每一个立方体的偏移位置
    public int offsetX=2;
    public int offsetY=2;

    //地图的宽高
    public int mapH=5;
    public int mapW=5;

    public Material red;
    public Material yello;
    public Material green;
    public Material normal;
    private Dictionary<string, GameObject> cubes = new Dictionary<string, GameObject>();

    //开始点,给它一个坐标为负的点
    public Vector2 beginPos = Vector2.right * -1;
    private List<AStarNode> list = new List<AStarNode>();

    private void Start()
    {
        AStarManager.Instance.InitMapInfo(mapW, mapH);

        for (int i= 0; i < mapW;++i)
        {
            for(int j=0;j<mapH;++j)
            {
                GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
                obj.transform.position = new Vector3(beginX + i * offsetX, beginY +j* offsetY, 0);
                //名字
                obj.name = i + "_" + j;
                cubes.Add(obj.name, obj);
                AStarNode node = AStarManager.Instance.nodes[i, j];
                if(node.type==NodeType.stop)
                {
                    obj.GetComponent<MeshRenderer>().material = red;
                }
            }
        }

    }
    private void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            RaycastHit inFo;
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            if(Physics.Raycast(ray,out inFo,1000))
            {
                if(beginPos==Vector2.right*-1)
                {
                    if (list != null)
                    {
                        for (int i = 0; i < list.Count; i++)
                        {
                            cubes[list[i].x + "_" + list[i].y].GetComponent<MeshRenderer>().material = normal;
                        }
                    }
                    string[] strs = inFo.collider.gameObject.name.Split('_');
                    beginPos = new Vector2(int.Parse(strs[0]), int.Parse(strs[1]));
                    inFo.collider.gameObject.GetComponent<MeshRenderer>().material = yello;
                }
                else
                {
                    string[] strs = inFo.collider.gameObject.name.Split('_');
                    Vector2 endPos = new Vector2(int.Parse(strs[0]), int.Parse(strs[1]));
                   // inFo.collider.gameObject.GetComponent<MeshRenderer>().material = yello;
                    list = AStarManager.Instance.FindPath(beginPos, endPos);
                    if(list!=null)
                    {
                        for(int i=0;i<list.Count;i++)
                        {
                            cubes[list[i].x + "_" + list[i].y].GetComponent<MeshRenderer>().material = green;
                        }
                    }
                    cubes[(int)beginPos.x + "_" +(int) beginPos.y].GetComponent<MeshRenderer>().material = normal;
                    beginPos = Vector2.right * -1;
                }
            }
        }
    }
}

3.实例演示

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

A星寻路算法的学习总结(详解) 的相关文章

  • STL不同容器的优缺点

    一 容器的分类 1 序列容器 xff08 1 xff09 vector 典型的序列容器 xff0c 任意元素的读取 修改具有O 1 xff0c 在序列尾部进行插入 删除是O 1 xff0c 但在序列的头部插入 删除的时间复杂度是O n xf
  • c语言(数组)

    交换算法 xff08 将最小值换到第一位 xff0c 最大值换到最后一位 xff09 include lt stdio h gt void main int o 61 0 int buf 10 接收用户输入的数组 for o lt 10 o
  • STM32外设串口资源用完了怎么办--------串口模拟解决问题(再也不用多个STM32或其它MCU)

    之前做项目的时候遇到了一个问题 xff0c 当把MCU本身的串口资源用完的时候 xff0c 却还需要使用多几个串口 xff0c 又不想使用几个MCU解决这个问题 那么模拟串口是解决这个问题的一种方法 下图是我对串口通信时序图的个人理解 xf
  • scanf函数中的格式字符串及注意事项

    scanf函数称为格式输入函数 xff0c 即按用户指定的格式从键盘上把数据输入到指定的变量之中 scanf函数的一般形式为 xff1a scanf 格式控制字符串 地址表列 xff1b 格式字符串的一般形式为 xff1a 输入数据宽度 长
  • 【C/C++】标准库, STL, Boost等的联系

    Backto C C 43 43 Index 标准库 最最开始 只有 C 语言 使用着使用着 一些常用的功能被写成了库 各种组织都是自己私有的库 后来为了方便统一使用和交流 就制定了标准 标准里的库 就是 C 标准库 后来 C 43 43
  • 数组与链表的优缺点和区别

    1 数组 xff1a 数组是将元素在内存中连续存放 xff0c 由于每个元素占用内存相同 xff0c 可以通过下标迅速访问数组中任何元素 但是如果要 在数组中增加一个元素 xff0c 需要移动大量元素 xff0c 在内存中空出一 个元素的空
  • 堆空间与栈空间的区别

    1 栈区 xff08 stack xff09 xff1a 又编译器自动分配释放 xff0c 存放函数的参数值 xff0c 局部变量的值等 xff0c 其操作方式类似于数据结构的 栈 2 堆区 xff08 heap xff09 xff1a 一
  • strtok函数及其实现

    头文件 xff1a include lt string h gt 定义函数 xff1a char strtok char s const char delim 函数说明 xff1a strtok 用来将字符串分割成一个个片段 参数s 指向欲
  • 服务器和客户机的信息函数以及读写函数

    1 服务器和客户机的信息函数 xff08 1 xff09 字节转换函数 在网络上面有着许多类型的机器 xff0c 这些机器在表示数据的字节顺序是不同的 xff0c 比如i386芯片是低字节在内存地址的低 端 xff0c 高字节在高端 xff
  • TCP协议的拥塞控制机制

    最初的TCP协议只有基于窗口的流控制 xff08 flow control xff09 机制而没有拥塞控制机制 流控制作为接受方管理发送方发送 数据的方式 xff0c 用来防止接受方可用的数据缓存空间的溢出 流控制是一种局部控制机制 xff
  • 构造函数为什么不能是虚函数

    1 从存储空间角度 xff0c 虚函数对应一个指向vtable虚函数表的指针 xff0c 这大家都知道 xff0c 可是这个指向vtable的指针其实是存储在对象的内存空间的 问题出来了 xff0c 如果构造函数是虚的 xff0c 就需要通
  • openmv学习五:OLED

    首先需要将SSD1306x py这个文件放到OPENMV中 代码 from machine import I2C Pin 从 machine 模块导入 I2C Pin 子模块 from ssd1306x import SSD1306 I2C
  • 自旋锁的实现及优化

    自旋锁也是一种互斥锁 xff0c 和mutex锁相比 xff0c 它的特点是不会阻塞 xff0c 如果申请不到锁 xff0c 就会不断地循环检测锁变量的状态 xff0c 也就是自旋 自旋锁的实现算法大多使用TAS算法或者CAS算法 TAS算
  • C语言----详解字符串相关的库函数(建议收藏)

    文章目录 系列文章目录前言一 C语言相关字符串库函数一览表 二 strlen函数 三 strcpy函数四 strcat函数 五 strcmp函数 六 strncpy函数 七 strncat函数 八 strncmp函数 九 strstr函数
  • C/C++头文件与变量的声明和定义

    C C 43 43 头文件与变量的声明和定义 最近遇到了变量重复包含的问题 xff0c 才发现自己有好多知识已经模糊了 xff0c 真惭愧 首先说下头文件 xff0c 其实头文件对计算机而言没什么作用 xff0c 她只是在预编译时在 inc
  • C语言寄存器变量register

    用register声明的变量是寄存器变量 xff0c 是存放在CPU的寄存器里的 而我们平时声明的变量是存放在内存中的 虽说内存的速度已经很快了 xff0c 不过跟寄存器比起来还是差得远 寄存器变量和普通变量比起来速度上的差异很大 xff0
  • Curl(C++)使用教程

    1 Curl简介 2 Easy interface 3 Multi interface 1 Curl简介 libcurl作为是一个多协议的便于客户端使用的URL传输库 xff0c 基于C语言 xff0c 提供C语言的API接口 xff0c
  • GPS原始RMC数据解析之DDMM.MMMM

    环境 GPS BD 定位模块 1 模块输出数据如下 GNRMC 100756 000 V 4000 0005 N 11559 9745 E 6 21 223 00 050313 N 68 2 了解换算规则 ddmm mmmm规则和dd dd
  • VINS-Mono代码阅读笔记(三):vins_estimator中main函数分析

    在VINS Mono代码阅读笔记 xff08 一 xff09 xff1a 从Euroc数据集的运行开始 中我们已经初步知道了vins estimator中订阅和发布的topic xff0c 那么 xff0c 在研究vins estimato

随机推荐

  • VINS-Mono代码阅读笔记(十三):posegraph中四自由度位姿优化

    本篇笔记紧接着VINS Mono代码阅读笔记 xff08 十二 xff09 xff1a 将关键帧加入位姿图当中 xff0c 来学习pose graph当中的紧耦合优化部分 在重定位完成之后 xff0c 进行位姿图优化是为了将已经产生的所有位
  • VINS-Mono代码阅读笔记(十四):posegraph的存储和加载

    本篇笔记紧接着VINS Mono代码阅读笔记 xff08 十三 xff09 xff1a posegraph中四自由度位姿优化 xff0c 来分析位姿图的存储和加载 完整 xff08 也是理想的 xff09 的SLAM的使用应该是这样的 xf
  • Visual Studio中设置opencv环境

    图像处理的项目中 xff0c 每建立一个新的项目 xff0c 需要对环境重新设置 xff0c 本文记录一下自己在VS中设置环境的步骤 xff0c 也分享给相同的入门小白 本文侧重说明VS中调用opencv的环境设置步骤 xff0c open
  • STM32学习笔记(1)---工程创建

    文章目录 前言一 新建工程步骤1 点击Project gt New uVision Project2 选择芯片3 创建文件夹4 放入必要文件4 头文件关联 总结问题 前言 这是我第一次写博客 xff0c 而且对于STM32也只是初学 xff
  • 乌班图安装 Kalibr

    安装ROS Melodic 1 Installation 1 1 Configure your Ubuntu repositories http www 360doc com content 18 0417 15 54525756 7463
  • ubuntu16.04 安装 php7.0-curl

    sudo apt add repository ppa ondrej php 添加这个源 sudo apt get update sudo apt get install php7 0 curl 这时成功安装php7 0 curl
  • string、char *、char[] 相互转换转换

    点击打开原文链接 一 string 转 char 主要有三种方法可以将 str 转换为 char 类型 xff0c 分别是 xff1a data c str copy 1 data 方法 xff1a string str 61 34 hel
  • STM32F103标准库开发---Uart串口通信实验---函数发送和中断接收

    STM32F103标准库开发 目录 文章目录 一 Uart串口通信 函数发送 1 Uart串口发送 标准库 函数 单字节发送 2 Uart串口检测标志 标准库 函数 3 Uart串口函数发送具体程序 二 Uart串口通信 中断接收 1 Ua
  • Keil5----新建项目文件( .c文件 和 .h文件)

    前言 在使用 Keil5 编辑程序的时候 xff0c 一定需要新建几个文件 xff08 c文件 和 h文件 xff09 xff0c 在其中编写不同功能的程序 例如 xff1a 新建LED c和LED h文件 xff0c 实现LED灯闪烁的功
  • 编码和串口通信

    先了解字符串和bytes xff08 字节 xff09 字符串 xff1a python里的字符串就是文本 xff0c 用于与人类交互 xff0c 像这样 xff1a 阿拉伯数字 xff1a a 61 1234566454 英语 xff1a
  • vscode终端不显示,闪退问题解决(完整步骤)

    1 以管理员身份运行此程序 步骤 xff1a 1 1 找到该文件目录的文件图标 1 2 右键属性选择兼容性 1 3 选择更改所有用户的设置然后勾选以管理员身份运行此程序后重新打开vscode 2 在vscode修改配置文件 2 1 打开vs
  • Vue项目启动报错 error:cannot find module xxx

    原因 xff1a 无法找到项目依赖的某个模块 解决办法 xff1a 1 删掉存放模块的文件夹node module xff1b 2 执行清除缓存命令 npm cache clean xff1b 如果报错 xff0c 使用强制清除npm ca
  • OkHttp-(一)HttpUrl了解

    1 xff0c git地址 xff1a https github com square okhttp 2 xff0c 官网地址 xff1a https square github io okhttp Http作为现代应用程序的常用联网方式
  • 学习网络编程第一步,安装NetAssist网络调试助手

    x1f4d6 摘要 今天分享下 遇到 Request header is too large xff0c 如何解决 xff0c 欢迎关注 xff01 x1f91e 简单介绍 NetAssist 是一款免安装的网络调试助手工具 今天给大家带来
  • 初学STM32之串口通信

    文章目录 一 背景知识1 处理器与外部通信的两种方式2 串行通信的三种传输方式3 串行通信的通信方式 二 串口通信基础1 STM32的串口通信接口2 UART异步通信引脚连接方法3 UART异步通信方式特点4 串口异步通信需要定义的参数 三
  • 前端架构图解

  • Ubuntu 18.04快捷安装ROS Melodic及rosdep update time out的问题解决

    1 ROS快捷安装 以下安装指令汇总针对Ubuntu18 04的ROS Melodic版本 xff1a 强烈建议复制以下指令到新建的xxx sh文件中 xff0c 保存后给xxx sh权限 xff0c 然后执行脚本一路输入y等候安装完成 e
  • NVIDIA Jetson AGX Xavier学习笔记3——环境配置(pytorch、torchvision、cv2)

    最近研究中需要使用NVIDIA Jetson AGX Xavier人工智能开发组件 由于也是第一次接触相关硬件设备 xff0c 遇到了很多困难 在这里记录整个Jetson AGX Xavier组件的学习过程 其中很多内容网上有比较详细的教程
  • Linux网络编程——tcp实例

    题目 1 通过TCP协议实现多个client端可以并发连接到server xff0c client可获得server指定目录下的文件列表 span class hljs comment client c Created on 2016年11
  • A星寻路算法的学习总结(详解)

    目录 1 理论基础 1 1A星寻路是用来解决什么问题的 1 2A星寻路的基本原理 2 代码实现 2 1每个格子的信息 2 2A星寻路管理器 2 3测试代码 3 实例演示 1 理论基础 1 1A星寻路是用来解决什么问题的 A星寻路是用来计算玩