游戏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:
判断点在圆形范围应->比较距离的平方和半径的平方,每次判断就可以减少一次开方的运算。

下一篇博文将介绍黑板。

猜你喜欢

转载自www.cnblogs.com/KillerAery/p/10053817.html