自由学习记录(22)

最后再总结一下吧

虽然过程里很多细节也许我没有去管,毕竟现在就已经存在更好的解决方案了

但大致思想是了解了

A星是一种网格上的遍历方式,为了找到一个目标点和起点之间的要经过的最短节点组

里面更像是动态规划

每一次的遍历,都是当前点作为一种状态,然后分向自己的8个方向节点,

通过曼哈顿的估计加上当前节点的实际,得出一个权值:

实际的部分会一直累加,通过父节点的g,在一次一次的移动中一直累加

蓝色的格子都是在openlist里面待排序成为最小的那个,黑格子则是close

的确是很抽象,不好讲,硬要讲又花很多时间,没效率

a星里面对于一大堆的黑格子里,在黑格子里面再找出最好的走法,也是没有关注的

因为规则就是简单的依附在前一个开始节点上,谁的八个方向里最先有自己,谁就是自己的爹

准确来说,这也是所有算法的问题,为了解决某个问题,提出一个算法

而算法本身就是一个一个规则约束出来的

而规则一定是有方向性的,就像一个一个长短不一的矢量,最后共同构成了一个总算法,而这个总的算法矢量是指向问题的解决的

而当实际需要解决的数据发生变化的时候,每一条约束虽然看上去是产生了约束,但是实际上,只是方向性有重合

学一个算法就解决所有的问题,一定是不可能的,一个约束的添加势必带来反方向的负面影响

所以对算法,首先应该知道,这个算法的矢量方向到底在哪,然后学习里面的约束,以及组合约束的技巧

总觉得自己对某个算法的了解还不到位,其实也没什么必要

毕竟如果把最优的方向都写明白了,那所有人都会自然的理解的

关于阻挡后,节点的跳跃,这一点,通过openList达到了要求,通过排序之后,不断选取"权值"最低的那一个,选取之后会从openList里转移到closeList里,然后将该点作为新的发散点

向8个方向发散求权值,从而寻找最到达目标点的路径

找到最终位置之后立刻就会停下,所以最终位置的父节点已经是设置好的了

还有,对于跳点的父节点问题,很容易认为跳了点之后,和父节点之间的联系就断了

但关于这块,单个节点会像四个方向遍历,每个节点都已经设置好了父节点就是自己开始节点的这个状态

而且跳点一定有有"回退"感的,因为整体来说,从出发点开始计算各个 的权值,越到后面越大,即使发现走不通了,也会找当前最小的另一个点,会"收束"回原始那条正确路线

对于closeList,作用更在优化上,我只想着实现,所以对closeList的优化有忽略

更多的来说,递归的状态虽然是在每一个节点身上,但是递归的次序是在openList上,

在 A* 算法中,closeList 是非常有用的,它用于存储已经被评估过的节点,以避免重复的工作。虽然父节点确实保存了路径的连接,但 closeList 还是起到了不可替代的作用,具体来说:

  1. 避免重复探索: closeList 可以确保每个节点只被处理一次。A* 算法需要根据启发式函数和代价函数计算最短路径,但如果没有 closeList,同一个节点可能会多次被加入到 openList 中,从而导致重复计算。

  2. 避免回退: 即使你通过父节点追溯路径,closeList 也能防止算法重新评估或重新进入某些已经处理过的节点。否则,某些节点可能会被多次加入 openList,尤其是在某些情况下,例如地图上有障碍物,可能会出现更短的路径导致节点重新进入队列。

  3. 优化性能: closeList 可以有效地减少不必要的计算,尤其是在大规模的寻路问题中。当某个节点已经在 closeList 中时,A* 就知道它不需要再次计算。

总之,openlist实现主体功能,closelist去优化重复操作(不然为什么叫close呢)

更模糊化的,一个点,朝另一个点去,大概怎么走最短,怎么走可以到...

A星就这样告一段落了

代码有报错,,出问题了

重敲

Unity中的具象化代码

(怎么感觉vs变好用了?昨天刚说想换idea今天就起飙的好用,离谱)

小细节,vector3里面的参数传入的是float,只可升浮点,降浮点要强转

yield return null 是当前可以不用再继续执行了,等到下一帧的空闲时间区再继续执行

然后是写在结尾的(生成的比较少,所以好像一次全部都生成了)

效果展示...转个文章学学gif怎么做5款免费好用的Gif录屏神器_录屏gif-CSDN博客

A星的数据层面主要代码

代码里面存在第一个点没有加入关闭列表的问题,会造成父对象始终存在,while循环终止不了

还是思想更重要,的确有点钻牛角尖了,没有确定实现的需求,敲的代码很难产生一个规范

产生差异是很正常的,而差异的产生不利于自己写的过程里报错的解决(更多的还是心理层面对报错的接受程度比较弱)

而如果跟着敲,的确是可以达到不报错,但自己很难提炼里面应该学习到的东西

毕竟技术一定是会更好的,而现在学老的东西,实际中肯定也是用不上的,更多的是产生对问题的一个处理思路

如果判断到下一次在openlist里存在的点就是终点,就可以停了

不额外优化,就会每次在curNode里面判断(这里的优化是极微的,数据量一大和没有优化是一样的,如果不懂也不用太放心上)

还有就是额外增加返回值的功能,通过递归返回最后的值,只会是找到了和遍历完所有的点都没有,然后返回false

这是在功能实现之后的再优化部分

全网最详细Unity A星寻路算法讲解|第五期:死路的处理和Unity中实现寻路算法_哔哩哔哩_bilibili

A星算法的功能就算是实现了,其他的就是里面一些边边角角的可以省去的性能优化了

(这里的代码是有错误的,错在初始点没有加入closelist)

cs 复制代码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum EStartNodeType
{
    Walk,
    Stop
}
public class AStarNode
{
    public int x; 
    public int y;

    public EStartNodeType type;//格子类型

    public float f, g, h;//寻路消耗

    public AStarNode father;
    public AStarNode(int x, int y, EStartNodeType type)
    {
        this.x = x;
        this.y = y;
        this.type = type;

    }
}

public class AStarManager
{
    private int mapW;
    private int mapH;
    public AStarNode[,] mapNodes;
    public List<AStarNode> openList = new List<AStarNode>();
    public List<AStarNode> closeList = new List<AStarNode>();
    private static AStarManager instance;
    public static AStarManager GetInstance()
    {
        if (instance == null)
        {
            instance = new AStarManager();
        }
        return instance;
    }
    public void InitMap(int W, int H)
    {
        mapNodes=new AStarNode[W, H];
        mapH = H;
        mapW = W;
        for(int i = 0;i<W;i++)
        {
            for(int j = 0; j < H; j++)
            {
                mapNodes[i,j]=new AStarNode(i, j,EStartNodeType.Walk);
            }
        }
    }
    public void FindPath(Vector2 startPos, Vector2 endPos)
    {
        openList.Clear();
        closeList.Clear();
        int sx=Mathf.FloorToInt(startPos.x);
        int sy=Mathf.FloorToInt(startPos.y);
        int ex=Mathf.FloorToInt(endPos.x);
        int ey=Mathf.FloorToInt(endPos.y);
        if (IsMapExternal(sx, sy) || IsMapExternal(ex, ey))
        {
            Debug.LogError("起点和终点在地图外");
            return;
        }
        var startNode =mapNodes[sx, sy];
        var endNode = mapNodes[ex, ey];
        if(startNode.type==EStartNodeType.Stop||endNode.type==EStartNodeType.Stop)
        {
            Debug.LogError("起点或终点是阻挡");
            return;
        }
        FindEndPoint(sx,sy,ex,ey);
    }

    private void FindEndPoint(int sx, int sy, int ex, int ey)
    {
        var startNode = mapNodes[sx,sy];
        for (int i = -1; i < 2; i++)
        {
            for(int j = -1; j < 2; j++)
            {
                if (i == 0 && j == 0) continue;

                int cx=sx+i;
                int cy=sy+j;
                if(cx==ex && cy==ey)return;//找到终点了,可以停止遍历了,不用再创建新的周围的cur节点遍历了
                if(IsMapExternal(cx, cy))continue;//点在图外面

                var curNode = mapNodes[cx,cy];

                if(curNode.type==EStartNodeType.Stop)continue;//是阻挡就排除

                if(openList.Contains(curNode)||closeList.Contains(curNode)) continue;//已经有当过计算的点了

                //都满足的话就可以当正常点来用了
                curNode.father = startNode;
                float d = 1;
                if (i != 0 && j != 0) d = 1.4f;
                float g = startNode.g + d;//对准确的部分进行计算
                float h = Mathf.Abs(cx-sx)+Mathf.Abs(cy-sy);//曼哈顿距离计算
                float f = g + h;
                curNode.f = f;
                curNode.g = g;
                curNode.h = h;
                openList.Add(curNode);

            }
        }//对周围8个点的for循环遍历结束

        //取之前先检查openList里还有没有点可以取,没有的话也就可以让整个寻路停下了
        if(openList.Count== 0)
        {
            return;
        }

        //排序,取出寻路消耗最小的点,然后用这个最小的点去下一次的遍历
        openList.Sort((node1, node2) =>
        {
            return node1.f >= node2.f ? 1 : -1;
        });
        var minNode = openList[0];
        closeList.Add(minNode);
        openList.RemoveAt(0);

        FindEndPoint(minNode.x, minNode.y,ex,ey);

    }

    private bool IsMapExternal(int x, int y)
    {
        return true;
    }
}

Touch and speech may bridge the gap between bodies, but the chasm between souls endures.

unity里C#脚本,用命名空间框起来的那些类,在外部使用的话就必须要引用命名空间

目的是防止同名而已,这个问题不大,算是写的更规范的小细节

想要使用命名空间中类的脚本文件顶部,添加 using 语句来引用命名空间。例如,如果你的类在 MyNamespace 命名空间下,且你需要在另一个脚本中使用该类,你应该这样写:

cs 复制代码
using MyNamespace;

public class ExampleScript : MonoBehaviour
{
    void Start()
    {
        // 直接使用命名空间下的类
        MyClass myClass = new MyClass();
    }
}

或者使用类的完全限定名

cs 复制代码
public class ExampleScript : MonoBehaviour
{
    void Start()
    {
        // 使用完全限定名来访问命名空间中的类
        MyNamespace.MyClass myClass = new MyNamespace.MyClass();
    }
}
  • 如果类被命名空间封装,外部脚本需要通过 using 或完全限定名来引用。
  • using 语句可以简化类的引用,避免每次都写出完整的命名空间。

死路的情况,会让格子遍历完每个点,最后一定会结束,也算是遍历了所有的可能

就是错了,up主把ex和ey当做起点了,说的声音很小但是有

上一秒刚说到终点然后就赋值了起点的

不过结果碰巧也是达到了寻路的效果,为什么呢

手敲一遍吧,也算再熟悉一下了

也不是啊,他到最后都没改代码,是我理解错了?不过不得不说,他写代码的感觉真的很从容,一种老练的味道

idea也可以写c#脚本,对编译器不同的问题,问题不会很大的,不用太高估编译器的影响(不过idea写脚本的确会感觉好一点,vs感觉太淡了,没什么辅助功能加持)

开启列表记录一切可能

关闭列表用父节点回家

用这些人名去描述,可以少很多字就表述出自己的意思,舍弃了一些表达的精确度换取信息浓度的升高

(总觉得这些是过时的东西,要学就应该学最前沿的,仔细想想可能这样反而是更合适的,甚至对我现在的水平是刚刚好的)

应该是大致理解了,边界,red blob那个网站倒是给到了启发了

关注点不过度放在路线的每一步,而是自己可执行的边界的那些格子的判断

全网最详细Unity A星寻路算法讲解|第五期:死路的处理和Unity中实现寻路算法_哔哩哔哩_bilibili

一个很好的可以交互的a星讲解网站

Introduction to the A* Algorithm

a星是比较独立的功能,在哪个工程里都可以用...

如果是2d的寻路功能,a星寻路很好用


感觉学之前要对自己打个预防针吧,学这个东西可以不深,但是用到的时候要知道大致怎么用,也不要怕自己不够熟,然后功能实现的时候有点想刻意避开用这个

没有必要,如果这个东西可以被归为一个学习区域,本身的使用性或者仅仅是了解,也算是价值很高的

A星寻路算法的学习

相关推荐
做cv的小昊11 分钟前
【TJU】信息检索与分析课程笔记和练习(6)英文数据库检索—web of science
大数据·数据库·笔记·学习·全文检索
Darkershadow17 分钟前
蓝牙学习之uuid与mac
python·学习·ble
毛小茛27 分钟前
芋道管理系统学习——项目结构
java·学习
北岛寒沫1 小时前
北京大学国家发展研究院 经济学原理课程笔记(第二十五课 开放宏观基本概念)
经验分享·笔记·学习
科技林总2 小时前
【系统分析师】2.3 预测与决策
学习
q行2 小时前
java学习日志--IO流(使用)
java·学习·io流
头疼的程序员2 小时前
计算机网络:自顶向下方法(第七版)第二章 学习分享(一)
学习·计算机网络
先生沉默先2 小时前
TypeScript 学习_类型与语法(2)
学习·typescript
茶猫_3 小时前
C++学习记录-旧题新做-链表求和
数据结构·c++·学习·算法·leetcode·链表
龘龍龙3 小时前
Python基础学习(十一)
python·学习·mysql