A*尋路演算法的C#實現

來源:互聯網
上載者:User

詳細講述了A*尋路演算法,有下列地方值得學習
1. 不要用AStarPathNode來構造PathFinder的Matrix,Matrix僅僅是byte[,],需要對Matrix中某個元素進行處理時再構造相應的AStarPathNode
2. 對每個Matrix中的元素取值不僅僅0和1,而是代表其權重,0表示障礙物,1表示平地,n表示高山,沼澤難以行走的地方,Weight越大越難以通過。節點G值會根據Weight進行計算。
     newG = parentNode.G + mGrid[newNode.X, newNode.Y];
3. 使用Closed List,用於判斷是否達到尋路次數的上限。如果Closed List中的節點數達到某個數值(Search Limit),就停止尋路。
4. 設定A* 演算法的參數,
    Search Limit :如果Closed List中的節點數達到某個數值(Search Limit),就停止尋路。
    Heuristic Formula:使用不同的演算法來計算節點的H值,確省為Manhattan演算法。
    Diagonals:是否處理對角線上的相鄰節點
       如果Diagonals = false,就意味著找到的路徑都是直線
     

     

     HeavyDiagonals:是否認為走對角線比走直線更遠,表現在演算法上就是:
       if(HeavyDiagonals)
           newNodeGValue = parentNode.G + Matrix[newNode.X, newNodeY]*2.41; //???為什麼不是根號2~~1.41
       else
           newNodeGValue = parentNode.G + Matrix[newNode.X, newNodeY]
     
   
    Punish Change Direction:是否希望找到的路徑看起來比較平滑,如果新節點和其父節點的走向與
父節點和父節點的父節點之間的走向不符,則增加新節點的G值。這裡要注意newG += 20。//為什麼是20???
    演算法為:
     if (PunishChangeDirection)
           mHoriz = (parentNode.X - parentNode.PX);   //???似乎還應該加上mVert = (parentNode.Y - parentNode.PY)
     newNodeGValue = //計算GValue
      if (PunishChangeDirection)
      {
           if ((newNode.X - parentNode.X) != 0)
           {
               if (mHoriz == 0)
                    newG += 20;
           }
           if ((newNode.Y - parentNode.Y) != 0)
           {
                  if (mHoriz != 0)                                //???在這裡使用mVert
                      newG += 20;
           }
       }
     

    Tie Breaker: 避免出現殊途同歸,輕微的改變H值
   if (mTieBreaker)
{
      int dx1 = parentNode.X - end.X;
      int dy1 = parentNode.Y - end.Y;
      int dx2 = start.X - end.X;
      int dy2 = start.Y - end.Y;
      int cross = Math.Abs(dx1 * dy2 - dx2 * dy1);
      newNode.H = (int) (newNode.H + cross * 0.001);
   }
不同的組合在不同的環境下具有最好的效能
5. 對相鄰節點的處理很巧妙
    用 direction = new sbyte[8,2]{ {0,-1} , {1,0}, {0,1}, {-1,0}, {1,-1}, {1,1}, {-1,1}, {-1,-1}}; 分別表示上,右,下,左,右上,右下,左下,左上這八個相鄰Node的座標x,y值的位移。
    比如:current node座標為x,y,這八個節點的座標可以這樣獲得
    for(int i=0; i< 8 ; i ++)
    {
         newNodeX = x + direction[i,0];
         newNodeX = x + direction[i,1];
    }

6. 不使用普通的array list儲存open node,否則,找出F值最小的open node會花去大量的時間,而是引入了Priority Queue作為open node的容器。把排序的複雜度分解在Push和Pop中,保證每次Pop出的都是最小的元素

7. 與Path Find無關。背景工作執行緒與UI的互動
public delegate void PathFinderDebugHandler(int fromX, int fromY, int x, int y, PathFinderNodeType type, int totalCost, int cost);

public class PathFinder : IPathFinder
{
    public event PathFinderDebugHandler PathFinderDebug;

}

//-------- Form
mPathFinder.PathFinderDebug += new PathFinderDebugHandler(PathFinderDebug);   //handle Event

//用delegate來進行跨線程調用
private delegate void PathFinderDebugDelegate(int parentX, int parentY, int x, int y, PathFinderNodeType type, int totalCost, int cost);

private void PathFinderDebug(int parentX, int parentY, int x, int y, PathFinderNodeType type, int totalCost, int cost)
{
    if (InvokeRequired)
    {
        Invoke(new PathFinderDebugDelegate(PathFinderDebug), new object[]{parentX, parentY, x, y, type, totalCost, cost});
        return;
    }

    PnlGUI.DrawDebug(parentX, parentY, x, y, type, totalCost, cost);
}

對本文我進行了下列修改:
1.在遍曆當前節點的相鄰節點時,需要看這個相鄰節點是否已經在open list 或close list中,如果某個相鄰節點已經在OpenList中,就要看如果把這個相鄰節點的父節點修改為當前節點,是否會降低這個相鄰節點的G值,原文中使用了for 迴圈來檢查這個節點是否在list中
int foundInOpenIndex = -1;
for(int j=0; j<mOpen.Count; j++)
{
    if (mOpen[j].X == newNode.X && mOpen[j].Y == newNode.Y)
    {
        foundInOpenIndex = j;
        break;
    }
}
if (foundInOpenIndex != -1 && mOpen[foundInOpenIndex].G <= newG)
    continue;

int foundInCloseIndex = -1;
for(int j=0; j<mClose.Count; j++)
{
    if (mClose[j].X == newNode.X && mClose[j].Y == newNode.Y)
    {
        foundInCloseIndex = j;
        break;
    }
}
if (foundInCloseIndex != -1 && mClose[foundInCloseIndex].G <= newG)
    continue;

為了提高效能,我給儲存open node的Queue中加入
private Dictionary<Point, int> pointDictionary = new Dictionary<Point, int>();
並使用Dictionary<Point, AStarNode> closedNodes 儲存close node,
以便使用x,y座標可以在open list或closed list中找到相應的Node

2.為了避免出現這種”穿牆”的情況,我加入了一個條件”CrossDiagonalBar”,


效果如下
  

還有一個問題有待解決
如果目標節點本身為障礙物,或是目標節點被障礙物完全包圍,如何給出一條路徑,使其盡量逼近目標節點?

 

源碼下載請至置頂頁~~

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.