自由学习记录(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星寻路算法的学习

相关推荐
南宫生2 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
sanguine__2 小时前
Web APIs学习 (操作DOM BOM)
学习
数据的世界015 小时前
.NET开发人员学习书籍推荐
学习·.net
四口鲸鱼爱吃盐5 小时前
CVPR2024 | 通过集成渐近正态分布学习实现强可迁移对抗攻击
学习
OopspoO7 小时前
qcow2镜像大小压缩
学习·性能优化
A懿轩A7 小时前
C/C++ 数据结构与算法【栈和队列】 栈+队列详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·栈和队列
居居飒8 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
kkflash38 小时前
提升专业素养的实用指南
学习·职场和发展
1 9 J9 小时前
数据结构 C/C++(实验五:图)
c语言·数据结构·c++·学习·算法
6.9410 小时前
Scala——身份证号码查询籍贯
学习·scala