游戲AI之感知


視覺感知


視覺感知是一種常見的感知。
在許多即時戰略游戲或者類DOTA游戲里,一個單位的視覺感知往往是圓形范圍的。

扇形視野

當然在其他大部分俯視角游戲里,一個智能體的視覺感知應該是類似現實人眼觀看的扇形范圍。

對於橫板游戲,可以把視野“豎”起來,檢測方式無多少差別。
對於空間更加復雜的3D游戲,可能需要視錐體(立體)檢測。

一個更快的技巧是照樣做成扇形檢測,只是再額外增加高度差檢測(即看作2.5D處理)。

但是視野實際還需考慮阻擋問題,這里提供1種主流解決視野遮擋的思路:

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

進一步的優化則可以預先“規划”好區域,構建潛在可視集(PVS),盡可能過濾不必要的目標,縮小所在區域的潛在目標數量
(例如屋外看不到房內的人,也就可以過濾掉房內的人),那么檢測速度就非常快。

示例(C++):

//視野感知
class ViewPerception {
public:
	//進行一次視野感知探測
	void check(Vector2 position) {
		//先清理上次的結果
		perceptionResult.clear();

		//逐個潛在目標檢測
		for (Object* target : potentialTargets) {
			//運用簡單的數學運算判斷點是否在扇形范圍:

			//先進行距離判斷是否在半徑內。
			Vector2 offset = target.getPosition() - position;
			float distanceSq = offset.lengthSquare();
			if (distanceSq > radiusSq)continue;

			//look向量和射線單位向量的數量積絕對值 若大於 數量積限制,
			//則證明該射線離look向量的角度 超出數量積限制的對應角度。
			float dotproduct = fabs(offset.normalize().dot(look));
			if (dotproduct > dotproductlimit)continue;

			//最后使用射線檢測第一個碰到的物體是不是目標物體
			//若射線第一個碰到的物體是目標物體,則可視為 看見了該物體
			if (raycast(position, target->position).result.object == target) {
				perceptionResult.emplace_back(target);
			}
		}
	}
private:
	Vector2 look;				//朝前的單位向量
	float radiusSq;				//扇形半徑的平方
	float dotproductlimit;		//數量積限制
	std::vector<Target*> perceptionResult;	//感知到的目標(結果)
	//....
};

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

橢圓視野

上述扇形視野有幾個缺陷:

  • 智能體應該能看到貼近側方的物體(甚至能感知到貼近背后的位置)
  • 智能體對於正前方向應該能看的更遠

基於這些缺陷,我們加了一個圓形和狹長的扇形視野范圍(紫色點為智能體):

但是這樣計算量就提升了不少,一個代替方法是使用橢圓型視野(紫色點為智能體):

橢圓(任意轉向)的長軸長為2a,短軸長為2b,兩個焦點離圓心的距離是c和-c(而且\(c^2=a^2-b^2\))。
橢圓上任意一點到兩個焦點的距離之和必等於2a,利用這個性質可推理出:
若某個點與橢圓上兩個焦點距離之和小於2a,則必在橢圓內。

因此我們只要預設好常量值:

  • a:取決於視野的長度
  • b:取決於視野的寬度
  • c:由\(c^2=a^2-b^2\)計算出
  • d1,d2:在智能體位置正前方d1距離的點為焦點c1,正前方d2距離的點為焦點c2。
bool ViewPerception::checkPointInEllipse(Vector2 targetPosition){
  Vector2 c1 = this->position + this->look * d1;
  Vector2 c2 = this->position + this->look * d2;

  if(distance(c1,targetPosition)+distance(c2,targetPosition) <= 2*a)return true;
  
  return false;
}

橢圓型的視野不僅能解決上述缺陷,在現實中也更貼近人類視覺的模型,計算量也只略高於一個扇形視野計算。

基於分片的高性能視域搜索系統

在很多策略游戲里,視域(Line-of-Sight,簡稱LOS)是很重要的概念。
在典型RTS游戲里,視域分為可見區域,不可見區域,已探索(但不可見)區域,說白了就是戰爭迷霧機制。

為了實現視域系統,我們先把游戲世界分為一個個整齊的分片(可以是正方形網格,六邊形...)。
當我們檢測某個分片是否可見時,直觀的做法是直接判斷該分片位置是否位於玩家視野幾何形狀。潛在的問題是,分片越多需要檢測的次數呈幾何級數增長。

而更高性能的做法是:

1. 首先每個分片記錄一個數值(一般是用二維數組記錄),用於記錄該分片是否可視。

在實現時為滿足更復雜的需求可以記錄額外的數據:

  • 多單位視野共享,應該用一個計數,當其中一個單位不再看見該分片時,可以減少計數,而不是直接修改為不可視。
  • 多方視野,應該用一個(可能多個)Byte值,其中每個位表示某方視野是否可見。這可以用在觀戰系統,隨時屏蔽某一方或者只關注某一個玩家的視野。
  • 多種視域類型,例如可見區域,不可見區域,已探索區域...則得記錄枚舉值。

2. 每幀將玩家的舊視野(上一幀的視野)對應的所有分片數值修改為不可視,然后根據新視野(當前幀的視野)對應的所有分片數值修改為可視。

在修改的時候,我們可以用一個LOS模板來幫助我們快速找到視野分片,並修改之。
這個LOS模板實際上就是列表數組,每一行記錄該行所有視野分片的位置:

for(int i = 0 ; i < LOStemplate.size() ; ++i){
  for(int j = 0 ; j < LOStemplate[i].size(); ++j){
    tiles[positionX + i - offset][positionY + LOStemplate[i][j]] = true;
  }
}

得益於LOS模板,我們不僅可以引入圓形LOS,還可以引入類似手電筒視野的LOS:

由於游戲里玩家可能轉向,對於一些非圓形LOS,我們可以准備多個LOS模板(例如對應90°,60°,30°..方向的LOS模板):

因為LOS模板完全可以通過預計算先算出來,所以使用它的CPU開銷只與它的視野分片數相關而不是與地圖分片數相關,這個性能開銷已經很不錯了。

3. 當需要檢測分片是否可見時,直接訪問記錄來獲取。

一個技巧是,不要主動搜索,而是利用分片記錄來主動通知:
例如當一個單位需要搜索視野內的一個敵人時,不是在該單位的LOS模板范圍內主動遍歷搜索敵人所在的分片,
而是敵人自己根據當前位置的分片數據(多方視野記錄),主動通知可看到該分片的單位。

簡單來說,思想是基於事件驅動而非輪詢,效率也提升的相當不錯。

聽覺感知


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

另外的,聽覺感知一般需要得到的信息:

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

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

示例(C++):

//聽覺感知
class ListenPerception {
public:
	//進行一次聽力感知探測
	void check(Vector2 position) {

		perceptionResult.clear();

		//逐個潛在聲源檢測
		for (Voice& voice : potentialVoices) {
			//判斷目標點是否在圓形范圍,即距離是否在半徑內。
			Vector3 offset = voice.getPosition() - position;
			float distanceSq = offset.lengthSquare();
			if (distanceSq > radiusSq)continue;

			//實際聲音大小會隨着距離增大而衰減
			float volume = voice.getVolume() / distanceSq;

			perceptionResult.emplace_back(voice.getTarget(),volume);
		}
	}
private:
	float radiusSq;			//范圍半徑
	std::vector<std::pair<Target*, float>> perceptionResult;	//感知到的目標+實際聲音大小(結果)
};

其它感知


這個其實應該叫雜項感知,因為一般來說,視覺感知和聽力感知已經足夠一個基本的智能體所需感知了。

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


游戲AI 系列文章:https://www.cnblogs.com/KillerAery/category/1229106.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM