忍者ブログ

神戸電子専門学校ゲームソフト学科の生徒が運営するGESのブログです。

   

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

A*(エースター)経路探索アルゴリズムやってみた

お疲れ様です ナトル です。

今回は有名な経路探索アルゴリズムの1つ「A*(エースター)」をやってみたので紹介します。

A* の特徴は、ある地点における
「スタートからその地点までの移動コスト」「その地点からゴールまでの移動コスト」
という2つの情報を使って経路を探すという事です。

拍手[1回]


・メリット
スタートからゴールまでの最短の経路を導き出すことができます。

・デメリット
計算負荷が高いです、スタート地点とゴール地点が離れるほど処理が重くなります。


早速Wikipediaやネットのソースコードを見ながら作ったC++コードを以下に掲載します。
クラスメンバへの直接アクセスや、インデントが深い、処理に無駄がある等、
簡略化したらかなり悲惨なコードになり、公開をスゴくためらいましたが思い切って載せます。

キャラクターのAI処理や経路探査に興味がある人の助けになれれば幸いです、超救われます。

//-----------------------------------------------
// A*アルゴリズムによる経路地点クラス
//-----------------------------------------------
class CAStarPoint
{
public:
  // コンストラクタ
  CAStarPoint( void )
    : m_vPos(D3DXVECTOR3(0.0f,0.0f,0.0f))
    , m_eStatus(NONE)
    , m_fG(0.0f)
    , m_fH(0.0f)
    , m_pParent(NULL)
  {
  }

  // デストラクタ
  ~CAStarPoint( void ){}

  // 親を設定しコストgを計算
  void SetParent( CAStarPoint* _pParent )
  {
    D3DXVECTOR3 vVec = m_vPos - _pParent->m_vPos;
    m_fG = _pParent->m_fG + D3DXVec3Length(&vVec);
    m_pParent = _pParent;
  }

  // コストhを計算
  void SetHCost( CAStarPoint* _pPoint )
  {
    D3DXVECTOR3 vVec =_pPoint->m_vPos - m_vPos;
    m_fH = D3DXVec3Length(&vVec);
  }

//private:
  //===============================================
  // 地点データ
  //===============================================
  D3DXVECTOR3 m_vPos; // 座標
  //===============================================
  // 経路データ
  //===============================================
  // 地点の状態定数
  enum EStatus
  {
    NONE, // 設定なし
    OPEN, // オープン
    CLOSED // クローズ
  };

  EStatus m_eStatus; // 探査状態
  std::list<CAStarPoint*> m_ltAdjacent; // 隣接ノードのリスト
  
  float m_fG; // スタートノードからこの地点までの移動コストの推定値
  float m_fH; // この地点からゴールノードまでの移動コストの推定値
  CAStarPoint* m_pParent; // 親のポインタ
  };

//-----------------------------------------------
// 経路を検索する 成功時はTRUE 失敗はFALSEを返す
//-----------------------------------------------
BOOL RouteSearch(
  std::list<CAStarPoint*>& _ltOut, // 経路を格納するリスト
  CAStarPoint* _pStart,// スタート地点
  CAStarPoint* _pGoal)// ゴール地点
{
  // 1. Open,Closeリストを用意
  std::list<CAStarPoint*> ltOpen; // 計算中のノードが格納される
  std::list<CAStarPoint*> ltClose; // 計算済みのノードが格納される
  
  // 2. スタート地点をOpenリストに追加
  _pStart->m_fG = 0.0f;
  _pStart->SetHCost(_pGoal);
  _pStart->m_eStatus = CAStarPoint::OPEN;
  ltOpen.push_back(_pStart);
  
  // 3. Openリストが空の時は探索失敗
  while( ltOpen.size() > 0 )
  {
    // 4. Openリストの中から最短でゴールに行けるかもしれない地点pNを取得
    CAStarPoint* pN = NULL;
    {
      float f1 = FLT_MAX;
      std::list<CAStarPoint*>::iterator it = ltOpen.begin();
      while( it != ltOpen.end() )
      {
        float f2 = (*it)->m_fG + (*it)->m_fH;
        if ( f2 < f1 )
        {
          f1 = f2;
          pN = (*it);
        }
        ++it;
      }
    }
    
    // 5. pNがゴール地点だった時は経路を作成して終了
    if ( pN == _pGoal )
    {
      // ゴール地点の親の親の親の…とポインタをたどって行くと
      // 親がNULLに設定されているスタート地点に戻ってこれる
      CAStarPoint* pPoint = _pGoal;
      while( pPoint )
      {
        _ltOut.push_front(pPoint);
        pPoint = pPoint->m_pParent;
      }
      return TRUE;
    }
    
    // pNがゴールで地点ではなかったらクローズする
    ltOpen.remove( pN );
    pN->m_eStatus = CAStarPoint::CLOSED;
    ltClose.push_back(pN);
    
    // 6. pNに隣接する地点を調べる
    std::list<CAStarPoint*>::iterator it = pN->m_ltAdjacent.begin();
    while( it != pN->m_ltAdjacent.end() )
    {
      switch ( (*it)->m_eStatus )
      {
      case CAStarPoint::NONE:
        // オープンする
        (*it)->SetParent(pN);
        (*it)->SetHCost(_pGoal);
        (*it)->m_eStatus = CAStarPoint::OPEN;
        ltOpen.push_back((*it));
        break;
      case CAStarPoint::OPEN:
        {
          // 隣接地点に設定されている親ポインタをpNに変更したら
          // コストが少なくなる時はデータを更新する
          D3DXVECTOR3 vVec = (*it)->m_vPos - pN->m_vPos;
          float f = pN->m_fG + (*it)->m_fH + D3DXVec3Length(&vVec);
          if ( f < ( (*it)->m_fG+(*it)->m_fH ) )
          {
            (*it)->SetParent(pN);
          }
        }
        break;
      case CAStarPoint::CLOSED:
        {
          // 隣接地点に設定されている親ポインタをpNに変更したら
          // コストが少なくなる時はデータを更新し再度オープンする
          D3DXVECTOR3 vVec = (*it)->m_vPos - pN->m_vPos;
          float f = pN->m_fG + (*it)->m_fH + D3DXVec3Length(&vVec);
          if ( f < ( (*it)->m_fG+(*it)->m_fH ) )
          {
            (*it)->SetParent(pN);
            ltClose.remove( (*it) );
            (*it)->m_eStatus = CAStarPoint::OPEN;
            ltOpen.push_back((*it));
          }
        }
        break;
      }
      ++it;
    } // end while
  } // end while
  return FALSE;
}

以上がプログラムになります。
経路地点クラスに座標をセットし、隣接ノードをつなぎ合わせた後、RouteSearch()にスタート地点とゴール地点を渡せば、第1引数のリストに経路が格納される作りになっています。

※ブラウザでの見易さ重視のため全角空白が多用されています、コピペする場合は注意してください。


PR

COMMENT

NAME
TITLE
MAIL(非公開)
URL
EMOJI
Vodafone絵文字 i-mode絵文字 Ezweb絵文字
COMMENT
PASS(コメント編集に必須です)
SECRET
管理人のみ閲覧できます

ブログ内検索

最新コメント

[01/29 人面犬]
[10/01 8ch]
[09/12 uncle]
[09/10 某卒業生]
[06/07 uncle]

カレンダー

03 2024/04 05
S M T W T F S
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30

テスト

Copyright ©  -- GESブログ --  All Rights Reserved
Design by CriCri / Photo by Geralt / powered by NINJA TOOLS / 忍者ブログ / [PR]