1. 程式人生 > >Unity3d 實現 A * 尋路演算法

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的計算方式是直接計算距離