cocos2d-x的A*尋路

若是你還不熟悉A*尋路,請先看下這篇文章http://blog.csdn.net/dssdss123/article/details/11494065
java

1、先介紹幾個函數和結構:node

一、virtual void draw()app

這個函數跟與MFC上單文檔裏的OnDraw函數很像,這裏只是少了dc,這個函數會一直被調用,無需刷新,也就是說,你無需像在MFC上同樣調用Invalidate或者InvalidateRectiphone

二、virtual void ccTouchBegan(CCTouch* pTouch, CCEvent* pEvent)函數

這個函數是下響應觸摸的,當你點擊屏幕時,就會進到這個函數。要使這個函數有效,你須要在init中調用
setTouchEnabled(true);    // 容許該層響應觸摸
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, false);    // 註冊單點觸摸ui

在這個例子中,咱們不須要多點觸摸this

三、ccColor4F結構spa

這個結構在ccDrawSolidRect函數中將會使用到,ccDrawSolidRect是畫某種顏色的矩形,對應在MFC中,咱們使用的是FillSolidRect。ccColor4F是RGBA的結構,RGB是三顏色,紅綠藍,最後一個alpha值,他表示這個顏色的透明度。爲1是徹底不透明,0時則徹底透明。.net

2、實現code

一、去掉coco自帶的亂七八糟的顯示

1)去掉幀頻顯示

在函數bool AppDelegate::applicationDidFinishLaunching()中

// turn on display FPS
//pDirector->setDisplayStats(true);

將pDirector->setDisplayStats(true),註釋掉

2)去掉menu,Hello World文字標籤

在函數void HelloWorld::init()中

// Add the menu to HelloWorld layer as a child layer.
//this->addChild(pMenu, 1);


// Add the label to HelloWorld layer as a child layer.
//this->addChild(pLabel, 1);

將menu和label註釋掉

3)替換背景圖,並置於最底層

// 3. Add add a splash screen, show the cocos2d splash image.
CCSprite* pSprite = CCSprite::create("map.jpg");    // 將原先的HelloWorld.png,替換爲本身的圖片,這裏我換成map.jpg
CC_BREAK_IF(! pSprite);

二、初始化地圖

聲明結構表示格子掩碼等一些信息,咱們的例子中,只需掩碼,因此結構以下:

 

    struct ST_GRID
    {
        ST_GRID()  { gf = GRID_FLAG_DEFAULT; }
        GRID_FLAG gf;
    };
    typedef vector<ST_GRID*> VEC_GRID;
    VEC_GRID m_vecGrid;    // 保存地圖產生的全部格子

 

初始化地圖的格子掩碼,以下:

 

void HelloWorld::InitMap()
{
    // 初始化格子掩碼
    srand((unsigned int)time(NULL));
    for (int i = 0; i < GetRow() * GetCol(); i++)
    {
        int nRandFlag = ((int)(CCRANDOM_0_1() * 10)) % 4 == 0 ? GRID_FLAG_OBSTACLE : GRID_FLAG_DEFAULT;    // 十分之四的機率產生障礙
        ST_GRID* pGrid = new ST_GRID;
        if (!pGrid)
        {
            return ;
        }
        pGrid->gf = (GRID_FLAG)nRandFlag;
        m_vecGrid.push_back(pGrid);
    }
}


三、尋路

 

定義一個結構,用於尋路過程當中,記錄每一個格子的信息

 

    struct NODE
    {
        NODE()  {nIndex = 0; nG = 0; pParent = NULL;}
        int nIndex;
        int nG;
        NODE* pParent;
    };
    vector<NODE*> m_vecPath;    // 尋路的路徑

下面開始尋路

 

 

void HelloWorld::FindPath()
{
    vector<NODE*> vecClose;    // close表
    vector<NODE*> vecOpen;     // open表
    if (m_nStartIndex == -1 || m_nEndIndex == -1)
    {
        return ;
    }
    m_vecPath.clear();    // 這裏,咱們並無delete,但卻不會內存泄漏,由於cocos2d-x使用了跟java同樣的技術 -- 內存回收機制,自動處理垃圾

    // 先添加開始點
    NODE* pNode = new NODE;
    pNode->nIndex = m_nStartIndex;
    vecClose.push_back(pNode);

    int nStep = 0;
    while(true)
    {
        if (nStep++ >= 200)    // 最多尋200格
        {
            break;
        }
        NODE* pNextNode = vecClose[vecClose.size() - 1];    // 取下一個路徑
        if (!pNextNode)
        {
            break;
        }
        if (pNextNode->nIndex == m_nEndIndex)    // 找到終點,就再也不找了
        {
            break;
        }

        for (int i = 0; i < 8; i++)
        {
            int nIndex = GetIndexByDir(pNextNode->nIndex, i);    // 根據方向取索引
            if (-1 == nIndex)
            {
                continue;
            }
            if (m_vecGrid[nIndex]->gf == GRID_FLAG_OBSTACLE)    // 障礙
            {
                continue;
            }
            if (InTable(nIndex, vecClose) != NULL)      // 在close表裏
            {
                continue;
            }

            NODE* pNode = InTable(nIndex, vecOpen);     // 在open表裏,比較G值,取G值更小的爲新路徑
            if (pNode)
            {
                int nNewG = pNextNode->nG + GetGByIndex(pNextNode->nIndex, pNode->nIndex);
                if (pNode->nG > nNewG)
                {
                    pNode->nG = nNewG;
                    pNode->pParent = pNextNode;    // 改變節點的父節點
                }
                continue;
            }

            // 新搜索到的格子,添加到開放列表
            pNode = new NODE;
            pNode->nIndex = nIndex;
            pNode->nG = pNextNode->nG + GetGByIndex(pNextNode->nIndex, pNode->nIndex);
            pNode->pParent = pNextNode;
            vecOpen.push_back(pNode);
        }

        // 找下一個路徑,open表裏F值最小的就是了
        int nMinF = 0xFFFFFF;
        pNextNode = NULL;
        int nNextNodeIndex = 0;
        for (int i = 0; i < (int)vecOpen.size(); i++)
        {
            NODE* pNode = vecOpen[i];
            if (!pNode)
            {
                continue;
            }
            int nH = GetHByIndex(pNode->nIndex);    // 計算該點與終點的H值,即路徑長度
            int nF = nH + pNode->nG;    // F = H + G
            if (nF < nMinF)
            {
                nMinF = nF;
                pNextNode = pNode;
                nNextNodeIndex = i;
            }
        }
        // 找到F值最小的,放入close表,並從open表裏刪除
        if (nNextNodeIndex >= 0 && nNextNodeIndex < (int)vecOpen.size())
        {
            vecClose.push_back(pNextNode);
            vecOpen.erase(vecOpen.begin() + nNextNodeIndex);
        }
    }

    // 尋路結束,找最優路徑
    pNode = vecClose[vecClose.size() - 1];
    while (pNode)
    {
        m_vecPath.push_back(pNode);
        pNode = pNode->pParent;
    }
}

四、展現到界面上

 

 

void HelloWorld::draw()
{
    // 畫背景表格
    CCSize size = CCDirector::sharedDirector()->getWinSize();
    for (int i = 0; i < GetRow(); i++)
    {
        ccDrawLine(ccp(0, i * GRID_SIDELEN), ccp(size.width, i * GRID_SIDELEN));
    }
    for (int i = 0; i < GetCol(); i++)
    {
        ccDrawLine(ccp(i * GRID_SIDELEN, 0), ccp(i * GRID_SIDELEN, size.height));
    }

    // 畫特殊格子顏色
    // 尋路獲得的路徑
    for (int i = 0; i < (int)m_vecPath.size(); i++)
    {
        CCPoint ptObstacleLT;
        CCPoint ptObstacleRD;
        GetRectPointByIndex(m_vecPath[i]->nIndex, ptObstacleLT, ptObstacleRD);
        ccColor4F clrObstacle = {0, 1, 1, 0};
        ccDrawSolidRect(ptObstacleLT, ptObstacleRD, clrObstacle);
    }

    // 開始點
    CCPoint ptStartLT;
    CCPoint ptStartRD;
    GetRectPointByIndex(m_nStartIndex, ptStartLT, ptStartRD);
    ccColor4F clrStart = {1, 0, 0, 1};
    ccDrawSolidRect(ptStartLT, ptStartRD, clrStart);

    // 結束點
    CCPoint ptEndLT;
    CCPoint ptEndRD;
    GetRectPointByIndex(m_nEndIndex, ptEndLT, ptEndRD);
    ccColor4F clrEnd = {0, 1, 0, 1};
    ccDrawSolidRect(ptEndLT, ptEndRD, clrEnd);

    // 障礙
    for (int i = 0; i < (int)m_vecGrid.size(); i++)
    {
        if (m_vecGrid[i]->gf == GRID_FLAG_OBSTACLE)
        {
            CCPoint ptObstacleLT;
            CCPoint ptObstacleRD;
            GetRectPointByIndex(i, ptObstacleLT, ptObstacleRD);
            ccColor4F clrObstacle = {0, 0, 1, 1};
            ccDrawSolidRect(ptObstacleLT, ptObstacleRD, clrObstacle);
        }
    }
}

這裏,我只介紹幾個比較重要的函數,其餘的就不贅述了,資源已上傳到CSDN,但還沒顯示出來,等顯示出來了,再把連接發到此處,有疑問的童鞋留言哈

。。。。。。。。

啊,我仍是直接上源碼吧

 

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"
#include "Box2D/Box2D.h"
#include "SimpleAudioEngine.h"
USING_NS_CC;

enum STEP
{
    STEP_DEFAULT    = 0,
    STEP_STARTPOINT = 1,
    STEP_ENDPOINT   = 2,
};

enum GRID_FLAG
{
    GRID_FLAG_DEFAULT   = 0,        // 默承認經過
    GRID_FLAG_OBSTACLE  = 1,        // 障礙
};
const int GRID_SIDELEN = 20;    // 不能爲0
//////////////////////////////////////////////////////////////////////////
class HelloWorld : public cocos2d::CCLayer
{
public:
    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    virtual bool init();  

    // there's no 'id' in cpp, so we recommand to return the exactly class pointer
    static cocos2d::CCScene* scene();
    
    // a selector callback
    void menuCloseCallback(CCObject* pSender);

    // implement the "static node()" method manually
    CREATE_FUNC(HelloWorld);

public:
    virtual void draw();
    virtual bool ccTouchBegan(CCTouch* pTouch, CCEvent* pEvent);

private:
    void InitMap();

private:
    int GetRow();
    int GetCol();
    int GetIndexByPoint(CCPoint pt);
    void GetRectPointByIndex(int nIndex, CCPoint &ptLT, CCPoint &ptRD);

private:
    int m_nStartIndex;
    int m_nEndIndex;

    struct ST_GRID
    {
        ST_GRID()  { gf = GRID_FLAG_DEFAULT; }
        GRID_FLAG gf;
    };
    typedef vector<ST_GRID*> VEC_GRID;
    VEC_GRID m_vecGrid;

    struct NODE
    {
        NODE()  {nIndex = 0; nG = 0; pParent = NULL;}
        int nIndex;
        int nG;
        NODE* pParent;
    };
    vector<NODE*> m_vecPath;    // 尋路的路徑

public:
    void FindPath();

private:
    int GetIndexByDir(int nIndex, int nDir);
    int GetGByIndex(int nStartIndex, int nEndIndex);
    int GetHByIndex(int nIndex);
    NODE *InTable(int nIndex, vector<NODE*> &vecTbl);

private:
    int m_nStep;
};

#endif  // __HELLOWORLD_SCENE_H__

 

 

 

#include "HelloWorldScene.h"

using namespace cocos2d;

CCScene* HelloWorld::scene()
{
    CCScene * scene = NULL;
    do 
    {
        // 'scene' is an autorelease object
        scene = CCScene::create();
        CC_BREAK_IF(! scene);

        // 'layer' is an autorelease object
        HelloWorld *layer = HelloWorld::create();
        CC_BREAK_IF(! layer);

        // add layer as a child to scene
        scene->addChild(layer);
    } while (0);

    // return the scene
    return scene;
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    bool bRet = false;
    do 
    {
        //////////////////////////////////////////////////////////////////////////
        // super init first
        //////////////////////////////////////////////////////////////////////////

        CC_BREAK_IF(! CCLayer::init());

        //////////////////////////////////////////////////////////////////////////
        // add your codes below...
        //////////////////////////////////////////////////////////////////////////

        // 1. Add a menu item with "X" image, which is clicked to quit the program.

        // Create a "close" menu item with close icon, it's an auto release object.
        CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
            "CloseNormal.png",
            "CloseSelected.png",
            this,
            menu_selector(HelloWorld::menuCloseCallback));
        CC_BREAK_IF(! pCloseItem);

        // Place the menu item bottom-right conner.
        pCloseItem->setPosition(ccp(CCDirector::sharedDirector()->getWinSize().width - 20, 20));

        // Create a menu with the "close" menu item, it's an auto release object.
        CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);
        pMenu->setPosition(CCPointZero);
        CC_BREAK_IF(! pMenu);

        // Add the menu to HelloWorld layer as a child layer.
        //this->addChild(pMenu, 1);

        // 2. Add a label shows "Hello World".

        // Create a label and initialize with string "Hello World".
        CCLabelTTF* pLabel = CCLabelTTF::create("Hello World", "Arial", 24);
        CC_BREAK_IF(! pLabel);

        // Get window size and place the label upper. 
        CCSize size = CCDirector::sharedDirector()->getWinSize();
        pLabel->setPosition(ccp(size.width / 2, size.height - 50));

        // Add the label to HelloWorld layer as a child layer.
        //this->addChild(pLabel, 1);

        // 3. Add add a splash screen, show the cocos2d splash image.
        CCSprite* pSprite = CCSprite::create("map.jpg");
        CC_BREAK_IF(! pSprite);

        // Place the sprite on the center of the screen
        pSprite->setPosition(ccp(size.width/2, size.height/2));

        // Add the sprite to HelloWorld layer as a child layer.
        this->addChild(pSprite, -1);

        setTouchEnabled(true);
        CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0,false);

        m_nStep = STEP_STARTPOINT;
        m_nStartIndex = -1;
        m_nEndIndex = -1;
        InitMap();

        bRet = true;
    } while (0);

    return bRet;
}

void HelloWorld::menuCloseCallback(CCObject* pSender)
{
    // "close" menu item clicked
    CCDirector::sharedDirector()->end();
}

//////////////////////////////////////////////////////////////////////////
void HelloWorld::InitMap()
{
    srand((unsigned int)time(NULL));
    for (int i = 0; i < GetRow() * GetCol(); i++)
    {
        int nRandFlag = ((int)(CCRANDOM_0_1() * 10)) % 4 == 0 ? GRID_FLAG_OBSTACLE : GRID_FLAG_DEFAULT;
        ST_GRID* pGrid = new ST_GRID;
        if (!pGrid)
        {
            return ;
        }
        pGrid->gf = (GRID_FLAG)nRandFlag;
        m_vecGrid.push_back(pGrid);
    }
}

//////////////////////////////////////////////////////////////////////////
void HelloWorld::FindPath()
{
    vector<NODE*> vecClose;
    vector<NODE*> vecOpen;
    if (m_nStartIndex == -1 || m_nEndIndex == -1)
    {
        return ;
    }
    m_vecPath.clear();

    // 先添加開始點
    NODE* pNode = new NODE;
    pNode->nIndex = m_nStartIndex;
    vecClose.push_back(pNode);

    int nStep = 0;
    while(true)
    {
        if (nStep++ >= 200)
        {
            break;
        }
        NODE* pNextNode = vecClose[vecClose.size() - 1];
        if (!pNextNode)
        {
            break;
        }
        if (pNextNode->nIndex == m_nEndIndex)
        {
            break;
        }

        for (int i = 0; i < 8; i++)
        {
            int nIndex = GetIndexByDir(pNextNode->nIndex, i);
            if (-1 == nIndex)
            {
                continue;
            }
            if (m_vecGrid[nIndex]->gf == GRID_FLAG_OBSTACLE)    // 障礙
            {
                continue;
            }
            if (InTable(nIndex, vecClose) != NULL)      // 在close表裏
            {
                continue;
            }

            NODE* pNode = InTable(nIndex, vecOpen);     // 在open表裏
            if (pNode)
            {
                int nNewG = pNextNode->nG + GetGByIndex(pNextNode->nIndex, pNode->nIndex);
                if (pNode->nG > nNewG)
                {
                    pNode->nG = nNewG;
                    pNode->pParent = pNextNode;
                }
                continue;
            }

            // 新搜索到的格子
            pNode = new NODE;
            pNode->nIndex = nIndex;
            pNode->nG = pNextNode->nG + GetGByIndex(pNextNode->nIndex, pNode->nIndex);
            pNode->pParent = pNextNode;
            vecOpen.push_back(pNode);
        }

        int nMinF = 0xFFFFFF;
        pNextNode = NULL;
        int nNextNodeIndex = 0;
        for (int i = 0; i < (int)vecOpen.size(); i++)
        {
            NODE* pNode = vecOpen[i];
            if (!pNode)
            {
                continue;
            }
            int nH = GetHByIndex(pNode->nIndex);
            int nF = nH + pNode->nG;
            if (nF < nMinF)
            {
                nMinF = nF;
                pNextNode = pNode;
                nNextNodeIndex = i;
            }
        }
        if (nNextNodeIndex >= 0 && nNextNodeIndex < (int)vecOpen.size())
        {
            vecClose.push_back(pNextNode);
            vecOpen.erase(vecOpen.begin() + nNextNodeIndex);
        }
    }

    // 尋路結束,找最優路徑
    pNode = vecClose[vecClose.size() - 1];
    while (pNode)
    {
        m_vecPath.push_back(pNode);
        pNode = pNode->pParent;
    }
}

//////////////////////////////////////////////////////////////////////////
int HelloWorld::GetIndexByDir(int nIndex, int nDir)
{
    if (nIndex < 0 || nIndex >= (int)m_vecGrid.size())
    {
        return -1;
    }

    int nRow = nIndex / GetCol();
    int nCol = nIndex % GetCol();

    switch(nDir)
    {
    case 0:     // 上
        nRow += 1;
        break;
    case 1:     // 右上
        nRow += 1;
        nCol +=1;
        break;
    case 2:     // 右
        nCol += 1;
        break;
    case 3:     // 右下
        nRow -= 1;
        nCol += 1;
        break;
    case 4:     // 下
        nRow -= 1;
        break;
    case 5:     // 左下
        nRow -= 1;
        nCol -= 1;
        break;
    case 6:     // 左
        nCol -= 1;
        break;
    case 7:     // 左上
        nRow += 1;
        nCol -= 1;
        break;
    default:
        break;
    }
    if (nRow < 0 || nRow >= GetRow()
        || nCol < 0 || nCol >= GetCol())
    {
        return -1;
    }
    return nRow * GetCol() + nCol;
}

//////////////////////////////////////////////////////////////////////////
int HelloWorld::GetGByIndex(int nStartIndex, int nEndIndex)
{
    int nStartRow = nStartIndex / GetCol();
    int nStartCol = nStartIndex % GetCol();

    int nEndRow = nEndIndex / GetCol();
    int nEndCol = nEndIndex % GetCol();

    if (nStartRow == nEndRow || nStartCol == nEndCol)
    {
        return 10;
    }
    return 14;
}

//////////////////////////////////////////////////////////////////////////
int HelloWorld::GetHByIndex(int nIndex)
{
    int nRow = nIndex / GetCol();
    int nCol = nIndex % GetCol();

    int nEndRow = m_nEndIndex / GetCol();
    int nEndCol = m_nEndIndex % GetCol();

    return (abs(nEndRow - nRow) + abs(nEndCol - nCol))*10;
}

//////////////////////////////////////////////////////////////////////////
HelloWorld::NODE *HelloWorld::InTable(int nIndex, vector<NODE*> &vecTbl)
{
    for (int i = 0; i < (int)vecTbl.size(); i++)
    {
        if (nIndex == vecTbl[i]->nIndex)
        {
            return vecTbl[i];
        }
    }
    return NULL;
}

//////////////////////////////////////////////////////////////////////////
int HelloWorld::GetRow()
{
    CCSize size = CCDirector::sharedDirector()->getWinSize();
    return size.height / GRID_SIDELEN;
}

//////////////////////////////////////////////////////////////////////////
int HelloWorld::GetCol()
{
    CCSize size = CCDirector::sharedDirector()->getWinSize();
    return size.width / GRID_SIDELEN;
}

//////////////////////////////////////////////////////////////////////////
int HelloWorld::GetIndexByPoint(CCPoint pt)
{
    pt.x = pt.x > (int)pt.x ? pt.x + 1 : pt.x;
    pt.y = pt.y > (int)pt.y ? pt.y + 1 : pt.y;
    return (int)pt.y / GRID_SIDELEN * GetCol() + (int)pt.x / GRID_SIDELEN;
}

//////////////////////////////////////////////////////////////////////////
void HelloWorld::draw()
{
    // 畫背景表格
    CCSize size = CCDirector::sharedDirector()->getWinSize();
    for (int i = 0; i < GetRow(); i++)
    {
        ccDrawLine(ccp(0, i * GRID_SIDELEN), ccp(size.width, i * GRID_SIDELEN));
    }
    for (int i = 0; i < GetCol(); i++)
    {
        ccDrawLine(ccp(i * GRID_SIDELEN, 0), ccp(i * GRID_SIDELEN, size.height));
    }

    // 畫特殊格子顏色
    // 尋路獲得的路徑
    for (int i = 0; i < (int)m_vecPath.size(); i++)
    {
        CCPoint ptObstacleLT;
        CCPoint ptObstacleRD;
        GetRectPointByIndex(m_vecPath[i]->nIndex, ptObstacleLT, ptObstacleRD);
        ccColor4F clrObstacle = {0, 1, 1, 1};
        ccDrawSolidRect(ptObstacleLT, ptObstacleRD, clrObstacle);
    }

    // 開始點
    CCPoint ptStartLT;
    CCPoint ptStartRD;
    GetRectPointByIndex(m_nStartIndex, ptStartLT, ptStartRD);
    ccColor4F clrStart = {1, 0, 0, 1};
    ccDrawSolidRect(ptStartLT, ptStartRD, clrStart);

    // 結束點
    CCPoint ptEndLT;
    CCPoint ptEndRD;
    GetRectPointByIndex(m_nEndIndex, ptEndLT, ptEndRD);
    ccColor4F clrEnd = {0, 1, 0, 1};
    ccDrawSolidRect(ptEndLT, ptEndRD, clrEnd);

    // 障礙
    for (int i = 0; i < (int)m_vecGrid.size(); i++)
    {
        if (m_vecGrid[i]->gf == GRID_FLAG_OBSTACLE)
        {
            CCPoint ptObstacleLT;
            CCPoint ptObstacleRD;
            GetRectPointByIndex(i, ptObstacleLT, ptObstacleRD);
            ccColor4F clrObstacle = {0, 0, 1, 1};
            ccDrawSolidRect(ptObstacleLT, ptObstacleRD, clrObstacle);
        }
    }
}

//////////////////////////////////////////////////////////////////////////
void HelloWorld::GetRectPointByIndex(int nIndex, CCPoint &ptLT, CCPoint &ptRD)
{
    ptLT.x = nIndex % GetCol() * GRID_SIDELEN;
    ptLT.y = nIndex / GetCol() * GRID_SIDELEN;
    ptRD.x = ptLT.x + GRID_SIDELEN;
    ptRD.y = ptLT.y + GRID_SIDELEN;
}

//////////////////////////////////////////////////////////////////////////
bool HelloWorld::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
{
    if (!pTouch)
    {
        return false;
    }

    int nIndex = GetIndexByPoint(pTouch->getLocation());
    if (m_vecGrid[nIndex]->gf == GRID_FLAG_OBSTACLE)
    {
        return false;
    }

    if (STEP_STARTPOINT == m_nStep)
    {
        m_nStartIndex = nIndex;
        m_nStep = STEP_ENDPOINT;
    }
    else if (STEP_ENDPOINT == m_nStep)
    {
        m_nEndIndex = nIndex;
        m_nStep = STEP_STARTPOINT;
        FindPath();
    }
    return true;
}

 

哎,想來想去,仍是直接上源碼比較直截了當。。。。。尋路效果以下,紅色是起點,綠色是終點,藍色是障礙物,淺藍色是最終尋路路徑:

相關文章
相關標籤/搜索