1. 程式人生 > >遊戲AI之感知(1)

遊戲AI之感知(1)

感知

視覺感知

視覺感知是一種常見的感知。

在許多即時戰略遊戲或者類DOTA遊戲裡,一個單位的視覺感知往往是圓形範圍的。

當然在其他大部分俯視角遊戲裡,一個智慧體的視覺感知應該是類似現實人眼觀看的扇形範圍

對於橫板遊戲,可以把視野“豎”起來,檢測方式無多少差別。

對於空間更加複雜的3D遊戲,可能需要視錐體形狀(6個平面)檢測而不是扇形或者圓錐形。
潛在的優化是照樣做成扇形檢測,只是再額外增加高度差檢測(即看作2.5D處理)。

但是視野實際還需考慮阻擋問題。
這裡提供兩種解決視野遮擋的思路:

  1. 在前方扇形範圍發出若干條射線進行檢測,若檢測到某個射線第一個碰到的物體是目標物體,則感知到該目標。

  1. 在所在區域的所有潛在目標進行遍歷,每次遍歷 先判斷是否在扇形範圍內,
    再做一條智慧體到目標的射線,若射線碰到的第一個物體是該目標,
    則感知到該目標。

第一個思路比較容易實現,第二個則演算法效率比較高。

第二個思路要是預先“規劃”好區域,儘可能過濾不必要的目標,縮小所在區域的潛在目標數量
(例如屋外看不到房內的人,也就可以過濾掉房內的人),那麼檢測速度就非常快。

聽力感知

聽力感知一般比較簡單粗暴:一個圓形/球形範圍檢測,
而且一般還無需考慮阻擋問題(現實中的聲音傳播可近似看作無阻擋)。

另外的,聽力感知一般需要得到的資訊:

  • 聲音來源(例如發出聲音的生物)
  • 聲音大小和距離

通過簡單的線性計算,由聲音大小和距離可以計算出實際接受聲音的大小。
將這個資訊作為額外資料交由決策使用。
(例如一個警衛,聽到太大的聲音就進入敵對狀態,小的聲音則進入警戒狀態)

其它感知

這個其實應該叫雜項感知或者根據需求隨便取名的感知。
一般來說,視覺感知和聽力感知已經足夠一個基本的智慧體所需感知了。

但極少情況還可能一些智慧體需要知道各種雜項資訊
(例如隊長給警衛傳送了一條無線電訊息,要求警衛趕往隊長所在位置支援)

實現

感知可以做成一種類,提供 檢測函式 和 結果訪問介面。

下面提供一種大致的示例(C++):

//視野感知
class ViewPerception {
public:
    //進行一次視野感知探測:
    void check(Vector3 position) {
        //先清理結果
        perceptionResult.clear();
        //逐個潛在目標檢測
        for (Biology* target : targets) {
            //運用簡單的數學運算判斷點是否在扇形範圍:
            //先進行距離判斷是否在半徑內。
            //look向量和射線單位向量的向量積 若小於 向量積限制,
            //則證明該射線離look向量的角度 超出向量積限制的對應角度。
            Vector3 offset = target.getPosition() - position;
            float distanceSq = offset.lengthSquare();
            if (distanceSq < radiusSq)continue;
            float crossproduct = offset.normalize().dot(look);
            if (crossproduct < dotproductlimit)continue;
            perceptionResult.emplace_back(target);
        }
    }
    //訪問感知目標結果
    const std::list<Biology*>& getResult()const {
        return perceptionResult;
    }
private:
    float radiusSq;             //扇形半徑的平方
    Vector3 look;               //朝前的單位向量
    float dotproductlimit;  //向量積限制
    std::list<Biology*> perceptionResult;   //使用連結串列儲存感知到的目標
};
//聽力感知
class ListenPerception {
public:
    //進行一次聽力感知探測:

    void check(Vector3 position) {
        perceptionResult.clear();
        //逐個潛在聲源檢測
        for (Voice& voice : voices) {
            //判斷目標點是否在圓形範圍,即距離是否在半徑內。
            Vector3 offset = voice.getPosition() - position;
            float distanceSq = offset.lengthSquare();
            if (distanceSq < radiusSq)continue;
            perceptionResult.emplace_back(voice.getBiology(),voice.getVolume()/distanceSq);
        }
    }
    //訪問感知目標結果
    const std::list<std::pair<Biology*, float>>& getResult()const {
        return perceptionResult;
    }
private:
    float radiusSq;         //範圍半徑
    std::list<std::pair<Biology*,float>> perceptionResult;  //使用連結串列儲存感知到的目標+實際聲音大小
};

TIP:
判斷點在圓形範圍應->比較距離的平方和半徑的平方,每次判斷就可以減少一次開方的運算。

下一篇博文將介紹黑板。