感知
视觉感知
视觉感知是一种常见的感知。
在许多即时战略游戏或者类DOTA游戏里,一个单位的视觉感知往往是圆形范围的。
当然在其他大部分俯视角游戏里,一个智能体的视觉感知应该是类似现实人眼观看的扇形范围
对于横板游戏,可以把视野“竖”起来,检测方式无多少差别。
对于空间更加复杂的3D游戏,可能需要视锥体形状(6个平面)检测而不是扇形或者圆锥形。
潜在的优化是照样做成扇形检测,只是再额外增加高度差检测(即看作2.5D处理)。
但是视野实际还需考虑阻挡问题。
这里提供两种解决视野遮挡的思路:
- 在前方扇形范围发出若干条射线进行检测,若检测到某个射线第一个碰到的物体是目标物体,则感知到该目标。
- 在所在区域的所有潜在目标进行遍历,每次遍历 先判断是否在扇形范围内,
再做一条智能体到目标的射线,若射线碰到的第一个物体是该目标,
则感知到该目标。
第一个思路比较容易实现,第二个则算法效率比较高。
第二个思路要是预先“规划”好区域,尽可能过滤不必要的目标,缩小所在区域的潜在目标数量
(例如屋外看不到房内的人,也就可以过滤掉房内的人),那么检测速度就非常快。
听力感知
听力感知一般比较简单粗暴:一个圆形/球形范围检测,
而且一般还无需考虑阻挡问题(现实中的声音传播可近似看作无阻挡)。
另外的,听力感知一般需要得到的信息:
- 声音来源(例如发出声音的生物)
- 声音大小和距离
通过简单的线性计算,由声音大小和距离可以计算出实际接受声音的大小。
将这个信息作为额外数据交由决策使用。
(例如一个警卫,听到太大的声音就进入敌对状态,小的声音则进入警戒状态)
其它感知
这个其实应该叫杂项感知或者根据需求随便取名的感知。
一般来说,视觉感知和听力感知已经足够一个基本的智能体所需感知了。
但极少情况还可能一些智能体需要知道各种杂项信息
(例如队长给警卫发送了一条无线电消息,要求警卫赶往队长所在位置支援)
实现
感知可以做成一种类,提供 检测函数 和 结果访问接口。
下面提供一种大致的示例(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:
判断点在圆形范围应->比较距离的平方和半径的平方,每次判断就可以减少一次开方的运算。
下一篇博文将介绍黑板。
原文地址:https://www.cnblogs.com/KillerAery/p/10053817.html