最后再总结一下吧
虽然过程里很多细节也许我没有去管,毕竟现在就已经存在更好的解决方案了
但大致思想是了解了
A星是一种网格上的遍历方式,为了找到一个目标点和起点之间的要经过的最短节点组
里面更像是动态规划
每一次的遍历,都是当前点作为一种状态,然后分向自己的8个方向节点,
通过曼哈顿的估计加上当前节点的实际,得出一个权值:
实际的部分会一直累加,通过父节点的g,在一次一次的移动中一直累加
蓝色的格子都是在openlist里面待排序成为最小的那个,黑格子则是close
的确是很抽象,不好讲,硬要讲又花很多时间,没效率
a星里面对于一大堆的黑格子里,在黑格子里面再找出最好的走法,也是没有关注的
因为规则就是简单的依附在前一个开始节点上,谁的八个方向里最先有自己,谁就是自己的爹
准确来说,这也是所有算法的问题,为了解决某个问题,提出一个算法
而算法本身就是一个一个规则约束出来的
而规则一定是有方向性的,就像一个一个长短不一的矢量,最后共同构成了一个总算法,而这个总的算法矢量是指向问题的解决的
而当实际需要解决的数据发生变化的时候,每一条约束虽然看上去是产生了约束,但是实际上,只是方向性有重合
学一个算法就解决所有的问题,一定是不可能的,一个约束的添加势必带来反方向的负面影响
所以对算法,首先应该知道,这个算法的矢量方向到底在哪,然后学习里面的约束,以及组合约束的技巧
总觉得自己对某个算法的了解还不到位,其实也没什么必要
毕竟如果把最优的方向都写明白了,那所有人都会自然的理解的
关于阻挡后,节点的跳跃,这一点,通过openList达到了要求,通过排序之后,不断选取“权值”最低的那一个,选取之后会从openList里转移到closeList里,然后将该点作为新的发散点
向8个方向发散求权值,从而寻找最到达目标点的路径
找到最终位置之后立刻就会停下,所以最终位置的父节点已经是设置好的了
还有,对于跳点的父节点问题,很容易认为跳了点之后,和父节点之间的联系就断了
但关于这块,单个节点会像四个方向遍历,每个节点都已经设置好了父节点就是自己开始节点的这个状态
而且跳点一定有有“回退”感的,因为整体来说,从出发点开始计算各个 的权值,越到后面越大,即使发现走不通了,也会找当前最小的另一个点,会“收束”回原始那条正确路线
对于closeList,作用更在优化上,我只想着实现,所以对closeList的优化有忽略
更多的来说,递归的状态虽然是在每一个节点身上,但是递归的次序是在openList上,
在 A* 算法中,
closeList
是非常有用的,它用于存储已经被评估过的节点,以避免重复的工作。虽然父节点确实保存了路径的连接,但closeList
还是起到了不可替代的作用,具体来说:
避免重复探索:
closeList
可以确保每个节点只被处理一次。A* 算法需要根据启发式函数和代价函数计算最短路径,但如果没有closeList
,同一个节点可能会多次被加入到openList
中,从而导致重复计算。避免回退: 即使你通过父节点追溯路径,
closeList
也能防止算法重新评估或重新进入某些已经处理过的节点。否则,某些节点可能会被多次加入openList
,尤其是在某些情况下,例如地图上有障碍物,可能会出现更短的路径导致节点重新进入队列。优化性能:
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)
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
命名空间下,且你需要在另一个脚本中使用该类,你应该这样写:
using MyNamespace;
public class ExampleScript : MonoBehaviour
{
void Start()
{
// 直接使用命名空间下的类
MyClass myClass = new MyClass();
}
}
或者使用类的完全限定名
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星寻路算法的学习