欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何用C++實(shí)現(xiàn)A*尋路算法

 更新時(shí)間:2021年06月16日 09:55:04   作者:KillerAery  
尋路是游戲比較重要的一個(gè)組成部分。因?yàn)椴粌HAI還有很多地方(例如RTS游戲里操控人物點(diǎn)到地圖某個(gè)點(diǎn),然后人物自動(dòng)尋路走過去)都需要用到自動(dòng)尋路的功能。本文將介紹一個(gè)經(jīng)常被使用且效率理想的尋路方法-A*尋路算法,并且提供額外的優(yōu)化思路

一、A*算法介紹

尋路,即找到一條從某個(gè)起點(diǎn)到某個(gè)終點(diǎn)的可通過路徑。而因?yàn)閷?shí)際情況中,起點(diǎn)和終點(diǎn)之間的直線方向往往有障礙物,便需要一個(gè)搜索的算法來解決。

有一定算法基礎(chǔ)的同學(xué)可能知道從某個(gè)起點(diǎn)到某個(gè)終點(diǎn)通常使用深度優(yōu)先搜索(DFS),DFS搜索的搜索方向一般是8個(gè)方向(如果不允許搜索斜向,則有4個(gè)),但是并無優(yōu)先之分。

為了讓DFS搜索更加高效,結(jié)合貪心思想,我們給搜索方向賦予了優(yōu)先級(jí),直觀上離終點(diǎn)最近的方向(直觀上的意思是無視障礙物的情況下)為最優(yōu)先搜索方向,這就是A*算法。

二、A*算法步驟解析

(如下圖,綠色為起點(diǎn),紅色為終點(diǎn),藍(lán)色為不可通過的墻。)

從起點(diǎn)開始往四周各個(gè)方向搜索。

(這里的搜索方向有8個(gè)方向)

為了區(qū)分搜索方向的優(yōu)先級(jí),我們給每個(gè)要搜索的點(diǎn)賦予2個(gè)值。

G值(耗費(fèi)值):指從起點(diǎn)走到該點(diǎn)要耗費(fèi)的值。

H值(預(yù)測(cè)值):指從該點(diǎn)走到終點(diǎn)的預(yù)測(cè)的值(從該點(diǎn)到終點(diǎn)無視障礙物情況下預(yù)測(cè)要耗費(fèi)的值,也可理解成該點(diǎn)到終點(diǎn)的直線距離的值)

在這里,值 = 要走的距離

(實(shí)際上,更復(fù)雜的游戲,因?yàn)榈匦尾煌ɡ缦葳?,難走的沙地之類的),還會(huì)有相應(yīng)不同的權(quán)值:值 = 要走的距離 * 地形權(quán)值)

我們還定義直著走一格的距離等于10,斜著走一格的距離等于14(因?yàn)?5°斜方向的長(zhǎng)度= sqrt(10^2+10^2) ≈ 14)

F值(優(yōu)先級(jí)值):F = G + H

這條公式意思:F是從起點(diǎn)經(jīng)過該點(diǎn)再到達(dá)終點(diǎn)的預(yù)測(cè)總耗費(fèi)值。通過計(jì)算F值,我們可以優(yōu)先選擇F值最小的方向來進(jìn)行搜索。

(每個(gè)點(diǎn)的左上角為F值,左下角為G值,右下角為H值)

計(jì)算出每個(gè)方向?qū)?yīng)點(diǎn)的F,G,H值后,

還需要給這些點(diǎn)賦予當(dāng)前節(jié)點(diǎn)的指針值(用于回溯路徑。因?yàn)橐恢彼严氯ニ训浇K點(diǎn)后,如果沒有前一個(gè)點(diǎn)的指針,我們將無從得知要上次經(jīng)過的是哪個(gè)點(diǎn),只知道走到終點(diǎn)最終耗費(fèi)的最小值是多少)

然后我們將這些點(diǎn)放入openList(開啟列表:用于存放可以搜索的點(diǎn))

然后再將當(dāng)前點(diǎn)放入closeList(關(guān)閉列表:用于存放已經(jīng)搜索過的點(diǎn),避免重復(fù)搜索同一個(gè)點(diǎn))

然后再從openList取出一個(gè)F值最小(最優(yōu)先方向)的點(diǎn),進(jìn)行上述同樣的搜索。

在搜索過程中,如果搜索方向上的點(diǎn)是障礙物或者關(guān)閉列表里的點(diǎn),則跳過之。

通過遞歸式的搜索,多次搜索后,最終搜到了終點(diǎn)。

搜到終點(diǎn)后,然后通過前一個(gè)點(diǎn)的指針值,我們便能從終點(diǎn)一步步回溯通過的路徑點(diǎn)。

(紅色標(biāo)記了便是回溯到的點(diǎn))

三、A*算法優(yōu)化思路

3.1、openList使用優(yōu)先隊(duì)列(二叉堆)

可以看到openlist(開啟列表),需要實(shí)時(shí)添加點(diǎn),還要每次取出最小值的點(diǎn)。

所以我們可以使用優(yōu)先隊(duì)列(二叉堆)來作為openList的容器。

優(yōu)先隊(duì)列(二叉堆):插入一個(gè)點(diǎn)的復(fù)雜度為O(logN),取出一個(gè)最值點(diǎn)復(fù)雜度為O(logN)

3.2、障礙物列表,closeList 使用二維表(二維數(shù)組)

由于障礙物列表和closeList僅用來檢測(cè)是否能通過,所以我們可以使用bool二維表來存放。

//假設(shè)已經(jīng)定義Width和Height分別為地圖的長(zhǎng)和寬
bool barrierList[Width][Height];
bool closetList[Width][Height];

有某個(gè)點(diǎn)(Xa,Yb),可以通過

if(barrierList[Xa][Yb]&&closeList[Xa][Yb])來判斷。

因?yàn)槎S表用下標(biāo)訪問,效率很高,但是耗空間比較多。(三維地圖使用三維表則更耗內(nèi)存。不過現(xiàn)在計(jì)算機(jī)一般都不缺內(nèi)存空間,所以盡量提升運(yùn)算時(shí)間為主)

這是一個(gè)典型的犧牲內(nèi)存空間換取運(yùn)算時(shí)間的例子。

3.3、深度限制

有時(shí)要搜的路徑非常長(zhǎng),利用A*算法搜一次付出的代價(jià)很高,造成游戲的卡頓。

那么為了保證每次搜索不會(huì)超過一定代價(jià),可以設(shè)置深度限制,每搜一次則深度+1,搜到一定深度限制還沒搜到終點(diǎn),則返還失敗值。

四、A*算法實(shí)現(xiàn)(C++代碼)

#include <iostream>
#include <list>
#include <vector>
#include <queue>

struct OpenPoint{
    int x;
    int y;
    int cost;                 // 耗費(fèi)值
    int pred;                 // 預(yù)測(cè)值
    OpenPoint* father;        // 父節(jié)點(diǎn)
    OpenPoint() = default;
    OpenPoint(int pX,int pY, int endX, int endY, int c, OpenPoint* fatherp) : x(pX),y(pY),cost(c), father(fatherp) {
        //相對(duì)位移x,y取絕對(duì)值
        int relativeX = std::abs(endX - pX);
        int relativeY = std::abs(endY - pY);
        //x,y偏移值n
        int n = relativeX - relativeY;
        //預(yù)測(cè)值pred = (max–n)*14+n*10+c
        pred = std::max(relativeX, relativeY) * 14 - std::abs(n) * 4 + c;
    }
};

//比較器,用以優(yōu)先隊(duì)列的指針類型比較
struct OpenPointPtrCompare {
    bool operator()(OpenPoint* a, OpenPoint* b) {
        return a->pred > b->pred;
    }
};

const int width = 30;            //地圖長(zhǎng)度
const int height = 100;          //地圖高度
char mapBuffer[width][height];   //地圖數(shù)據(jù)
int depth = 0;                   //記錄深度
const int depthLimit = 2000;     //深度限制
bool closeAndBarrierList[width][height];    //記錄障礙物+關(guān)閉點(diǎn)的二維表
//八方的位置
int direction[8][2] = { {1,0},{0,1},{-1,0},{0,-1},{1,1},{ -1,1 },{ -1,-1 },{ 1,-1 } };
//使用最大優(yōu)先隊(duì)列
std::priority_queue<OpenPoint*, std::vector<OpenPoint*>, OpenPointPtrCompare> openlist;
//存儲(chǔ)OpenPoint的內(nèi)存空間
std::vector<OpenPoint> pointList = std::vector<OpenPoint>(depthLimit);

//是否在障礙物或者關(guān)閉列表
inline bool inBarrierAndCloseList(int pX,int pY) {
    if (pX < 0 || pY < 0 || pX >= width || pY >= height)
        return true;
    return closeAndBarrierList[pX][pY];
}

//創(chuàng)建一個(gè)開啟點(diǎn)
inline OpenPoint* createOpenPoint(int pX,int pY,int endX,int endY, int c, OpenPoint* fatherp) {
    pointList.emplace_back(pX,pY,endX,endY, c, fatherp);
    return &pointList.back();
}

// 開啟檢查,檢查父節(jié)點(diǎn)
void open(OpenPoint& pointToOpen, int endX,int endY) {
    //將父節(jié)點(diǎn)從openlist移除
    openlist.pop();
    //深度+1
    depth++;
    //檢查p點(diǎn)八方的點(diǎn)
    for (int i = 0; i < 4; ++i)
    {
        int toOpenX = pointToOpen.x + direction[i][0];
        int toOpenY = pointToOpen.y + direction[i][1];
        if (!inBarrierAndCloseList(toOpenX,toOpenY)) {
            openlist.push(createOpenPoint(toOpenX, toOpenY, endX,endY, pointToOpen.cost + 10, &pointToOpen));
        }
    }
    for (int i = 4; i < 8; ++i)
    {
        int toOpenX = pointToOpen.x + direction[i][0];
        int toOpenY = pointToOpen.y + direction[i][1];
        if (!inBarrierAndCloseList(toOpenX, toOpenY)) {
            openlist.push(createOpenPoint(toOpenX, toOpenY, endX, endY, pointToOpen.cost + 14, &pointToOpen));
        }
    }
    //最后移入closelist
    closeAndBarrierList[pointToOpen.x][pointToOpen.y] = true;
}

//開始搜索路徑
std::list<OpenPoint*> findway(int startX,int startY, int endX,int endY) {
    std::list<OpenPoint*> road;
    // 創(chuàng)建并開啟一個(gè)父節(jié)點(diǎn)
    openlist.push(createOpenPoint(startX,startY, endX,endY, 0, nullptr));
    OpenPoint* toOpen = nullptr;
    //重復(fù)尋找預(yù)測(cè)和花費(fèi)之和最小節(jié)點(diǎn)開啟檢查
    while (!openlist.empty())
    {
        toOpen = openlist.top();
        // 找到終點(diǎn)后,則停止搜索
        if (toOpen->x == endX && toOpen->y ==endY) {break;}//若超出一定深度(1000深度),則搜索失敗
        if (depth >= depthLimit) {
            toOpen = nullptr;
            break;
        }
        open(*toOpen, endX,endY);
    }
    for (auto rs = toOpen; rs != nullptr; rs = rs->father) {road.push_back(rs);}
    return road;
}

//創(chuàng)建地圖
void createMap() {
    for (int i = 0; i < width; ++i)
        for (int j = 0; j < height; ++j) {
            //五分之一概率生成障礙物,不可走
            if (rand() % 5 == 0) {
                mapBuffer[i][j] = '*';
                closeAndBarrierList[i][j] = true;
            }
            else {
                mapBuffer[i][j] = ' ';
                closeAndBarrierList[i][j] = false;
            }
        }
}

//打印地圖
void printMap() {
    for (int i = 0; i < width; ++i) {
        for (int j = 0; j < height; ++j)
            std::cout << mapBuffer[i][j];
        std::cout << std::endl;
    }
    std::cout << std::endl << std::endl << std::endl;
}

int main() {
    //起點(diǎn)
    int beginX = 0;
    int beginY = 0;
    //終點(diǎn)
    int endX = 29;
    int endY = 99;
    //創(chuàng)建地圖
    createMap();
    //保證起點(diǎn)和終點(diǎn)都不是障礙物
    mapBuffer[beginX][beginY] = mapBuffer[endX][endY] = ' ';
    closeAndBarrierList[beginX][beginY] = closeAndBarrierList[endX][endY] = false;
    //A*搜索得到一條路徑
    std::list<OpenPoint*> road = findway(beginX,beginY,endX,endY);
    //將A*搜索的路徑經(jīng)過的點(diǎn)標(biāo)記為'O'
    for (auto& p : road){mapBuffer[p->x][p->y] = 'O';}
    //打印走過路后的地圖
    printMap();
    system("pause");
    return 0;
}

示例效果:

以上就是如何用C++實(shí)現(xiàn)A*尋路算法的詳細(xì)內(nèi)容,更多關(guān)于C++ A*尋路算法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 一篇文章帶你了解C++中的顯示轉(zhuǎn)換

    一篇文章帶你了解C++中的顯示轉(zhuǎn)換

    這篇文章主要介紹了C++11顯示類型轉(zhuǎn)換的優(yōu)點(diǎn),幫助大家更好的理解和學(xué)習(xí)c++,感興趣的朋友可以了解下,希望能給你帶來幫助
    2021-08-08
  • 面向?qū)ο笕筇匦缘囊饬x講解

    面向?qū)ο笕筇匦缘囊饬x講解

    今天小編就為大家分享一篇關(guān)于面向?qū)ο笕筇匦缘囊饬x講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • C++設(shè)計(jì)模式之建造者模式

    C++設(shè)計(jì)模式之建造者模式

    這篇文章主要介紹了C++設(shè)計(jì)模式之建造者模式,一個(gè)復(fù)雜對(duì)象是由多個(gè)部件組成的,建造者模式是把復(fù)雜對(duì)象的創(chuàng)建和部件的創(chuàng)建分別開來,分別用Builder類和Director類來表示,需要的朋友可以參考下
    2014-09-09
  • C語言實(shí)現(xiàn)校運(yùn)動(dòng)會(huì)項(xiàng)目管理系統(tǒng)

    C語言實(shí)現(xiàn)校運(yùn)動(dòng)會(huì)項(xiàng)目管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)校運(yùn)動(dòng)會(huì)項(xiàng)目管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • C++動(dòng)態(tài)聯(lián)編介紹

    C++動(dòng)態(tài)聯(lián)編介紹

    這篇文章主要介紹了C++動(dòng)態(tài)聯(lián)編,在C++中,聯(lián)編是指一個(gè)計(jì)算機(jī)程序的不同部分彼此關(guān)聯(lián)的過程。按照聯(lián)編所進(jìn)行的階段不同,可分為兩種不同的聯(lián)編方法:靜態(tài)聯(lián)編和動(dòng)態(tài)聯(lián)編
    2022-01-01
  • C++使用智能指針實(shí)現(xiàn)模板形式的單例類

    C++使用智能指針實(shí)現(xiàn)模板形式的單例類

    這篇文章主要為大家詳細(xì)介紹了C++使用了智能指針實(shí)現(xiàn)模板形式的單例類,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • C語言實(shí)現(xiàn)BMP圖像處理(彩色圖轉(zhuǎn)灰度圖)

    C語言實(shí)現(xiàn)BMP圖像處理(彩色圖轉(zhuǎn)灰度圖)

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)BMP圖像處理,彩色圖轉(zhuǎn)灰度圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • Visual?Studio2022的完全卸載及安裝到D盤的操作方法

    Visual?Studio2022的完全卸載及安裝到D盤的操作方法

    這篇文章主要介紹了Visual?Studio2022的完全卸載以及完全安裝到D盤,因?yàn)閂S如果隨便寫在會(huì)有很多很多的亂七八糟的東西掉出來,所以我們選擇制式一點(diǎn)的卸載方式,需要的朋友可以參考下
    2022-09-09
  • 數(shù)據(jù)結(jié)構(gòu)之矩陣行列和相等的實(shí)例

    數(shù)據(jù)結(jié)構(gòu)之矩陣行列和相等的實(shí)例

    這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu)之矩陣行列和相等的實(shí)例的相關(guān)資料,希望通過本文能幫助到大家,讓大家掌握這部分內(nèi)容,需要的朋友可以參考下
    2017-10-10
  • 詳解C語言中的自定義類型

    詳解C語言中的自定義類型

    這篇文章主要為大家詳細(xì)介紹了C語言中的四大自定義類型(結(jié)構(gòu)體、位段、枚舉和聯(lián)合)的相關(guān)知識(shí),文中的示例代碼簡(jiǎn)潔易懂,需要的可以參考一下
    2023-07-07

最新評(píng)論