Unity3d 实现 A * 寻路算法

原理

A* 算法是一种启发式搜索算法,通过代价函数f = g+h,A*算法每次查找代价最低的点作为搜索点,并更新搜索节点列表。最终搜索到目标位置。

需要定义两个列表 (Open和Closed列表)

    private List<Tile> openList = new List<Tile>();
    private List<Tile> closeList = new List<Tile>();
  •     一个记录下所有被考虑来寻找最短路径的格子(称为open 列表)
  •     一个记录下不会再被考虑的格子(成为closed列表)

首先在closed列表中添加当前位置(我们把这个开始点称为点 “A”)。然后,把所有与它当前位置相邻的可通行格子添加到open列表中。

引入二个概念:

节点(Node):每个格子都可以称为节点。

代价(Cost):描述角色移动到某个节点时所走的难易程度

通常寻路过程中的代价用f,g,h来表示

g代表从指定节点到相邻节点的代价

h代表从指定节点到目标节点根据不同的估价公式估算出来的代价。

而 f = g + h 表示节点的总代价

    public float FValue { get { return GValue + HValue; } }
    public float GValue { get; set; }
    public float HValue { get; set; }

通常障碍物本身也可以看成是由若干个不可通过的节点所组成,所以 CanPass 是用来标记该节点是否为障碍物(节点)。

在考查从一个节点移动到另一个节点时,总是拿自身节点周围的8个相邻节点来说事儿,相对于周边的节点来讲,自身节点称为它们的父节点

之后根据父节点回溯返回找到路径

    public Tile FatherTile;
    public bool CanPass;
    public List<Tile> nearTiles=new List<Tile>();

重复以下步骤来找到最短路径:

  1. 将Tile添加到open列表中,该列表有最小的和值。且将这个Tile称为T吧。
  2. 将T从open列表移除,然后添加T到closed列表中。
  3. 对于与T相邻的每一块可通行的方块nearT:

·        如果nearTclosed列表中或者是不可通过的:不管它。

·        如果nearT不在open列表中:添加它然后计算出它的和值。

·        如果T已经在open列表中:当我们使用当前生成的路径到达那里时,检查F和值是否更小。如果是,更新它的和值和它的父         节点。

    public List<Tile> SearchPath(Tile originTile, Tile targetTile)
    {
        Tile nowTile = originTile;
        openList.Add(nowTile);
        bool finded = false;
        while (openList.Count > 0)
        {
            nowTile = openList[0];
            for (int i = 0, max = openList.Count; i < max; i++)
            {
                if (openList[i].FValue <= nowTile.FValue &&
                    openList[i].HValue < nowTile.HValue)
                {
                    nowTile = openList[i];
                }
            }

            openList.Remove(nowTile);
            closeList.Add(nowTile);
            

            // 找到的目标节点
            if (nowTile.Equals(targetTile))
            {
                finded = true;
                break;
            }
            List<Tile> nearTiles = nowTile.nearTiles;
            // 判断周围节点,选择一个最优的节点
            foreach (Tile near in nearTiles)
            {
                // 如果是墙或者已经在关闭列表中
                if (!near.CanPass|| closeList.Contains(near))
                    continue;
                // 计算当前相领节点现开始节点距离
                float newValue = nowTile.GValue + nowTile.ComputeHValue(near);
                // 如果距离更小,或者原来不在开始列表中
                if (newValue < near.GValue || !openList.Contains(near))
                {
                    // 更新与开始节点的距离
                    near.GValue = newValue;
                    // 更新与终点的距离
                    near.HValue = near.ComputeHValue(targetTile);
                    // 更新父节点为当前选定的节点
                    near.FatherTile = nowTile;
                    // 如果节点是新加入的,将它加入打开列表中
                    if (!openList.Contains(near))
                    {
                        openList.Add(near);
                    }
                }
            }
        }
        openList.Clear();
        closeList.Clear();

        List<Tile> route = new List<Tile>();
        if (finded)
        {//找到后将路线存入路线集合  
            Tile tile = targetTile;
            while (!tile.Equals(originTile))
            {
                route.Add(tile);//将节点添加到路径列表里  

                Tile fatherHex = tile.FatherTile;//从目标节点开始搜寻父节点就是所要的路线  
                tile = fatherHex;
            }
            route.Add(tile);
        }
        route.Reverse();
        return route;
    }
ComputeHValue方法用于计算hCost,hCost的计算方式是直接计算距离


猜你喜欢

转载自blog.csdn.net/QQ734821120/article/details/80916276