阅读笔记-游戏开发中的人工智能-第4章-群聚

1. 基本群聚

    这一节介绍的算法,来自于Craig Reynolds在1987年发表的论文《Flocks,Herds and Schools:A Distributed Behavioral Model》。在论文中,他提出算法原型仿真整群的鸟、鱼或其他生物。
    这个算法的做出的行为非常类似水中的鱼群或成群的飞鸟。所有的“类鸟群”(指模拟的群体)可以同时朝同一方向移动,接着下一时刻,群聚形成的形体之尖端会转弯,而群体中其余的部分也会跟着转,形成的类鸟群在群体中传达转弯的行为,而形成波浪状。
    算法的三个规则:
    凝聚:每个单位都往其邻近单位的平均位置行动。
    对齐:每个单位行动时,都要把自己对齐在其邻近单位的平均方向上。
    分割:每个单位行动时,要避免撞上其邻近单位。
    单位视野
    两个参数决定每个单位的视野,半径R和角度θ。大的半径能让单位看到群体中的更多同伴,从而产生凝聚性更强的群体。也就是说,群体没有分裂成小群体的倾向。
    对于较小的视野,可能会产生类似一队蚂蚁行进的效果。比如:仿真一群战斗机,可能会使用大角度。仿真一直军队鬼鬼祟祟跟踪某人,可能会用小角度。 
               
                  图-单位的视野                                                                                             图-宽视野与窄视野

           
2. 群聚实例

2.1 行进模式
    这个实例考虑的是以物理机制为基础的范例,把每个单位视为刚体,通过在每个单位的前端施加转向力,来保证群聚的行进模式。每条规则都会影响施加的力,最终施加的力和方向是这些规则影响的综合。另外,需要考虑两件事:首先,要控制好每条规则贡献的转向力;其次,要调整行进模式,以确保每个单位都获得平衡。
    对于避开规则:为了让单位不会彼此撞上,且单位根据对齐和凝聚规则而靠在一起。当单位彼此间距离够宽时,避开规则的转向力贡献就要小一点;反之,避开规则的转向力贡献就要大一些。对于避开用的反向力,一般使用反函数就够用了,分隔距离越大,得出的避开用转向力越小;分隔距离越小,得出的避开用转向力越大。
    对于对齐规则:考虑当前单位当前方向,与其邻近单位间平均方向间的角度。如果该角度较小,我们只对其方向做小幅度调整,然而,如果角度较大,就需要较大的调整。为了完成这样的任务,可以把对齐用的转向力贡献,设定成和该单位方向及其邻近单位平均方向间的角度成正比。
2.2 邻近单位
    凝聚,对齐,分隔三个规则要起作用的前提是得到每个当前单位的邻近单位。邻近单位就是当前单位视野范围内的单位,需要从角度和距离两方面进行判断。
    首先根据角度检测,对于不同的视野检测方法是不同的(比如,宽视野,窄视野),下面的例子是对于宽广视野的检测
    ·计算d=目标单位位置-当前单位位置,w为d以目标单位反方向的坐标系中的向量坐标。
    ·如果w.y>0,则目标单位必然在视野范围内。
    ·如果w.y<0,则要看x,y坐标构成的线段斜率,是否在设定的视野区域之外,如果fabs(w.x)>fab(w.y)*_BACK_VIEW_ANGLE_FACTOR,则目标单位在视野范围内。其中的_BACK_VIEW_ANGLE_FACTOR就是视野角度系数。该系数等于1时,视野弧线的直线与x轴夹角是45度,该系数越大,两条线越接近x轴,不可见区域越大,反之,系数越小,两条线越接近y轴,不可见区域越小。
      
                                                                                  图-宽广视野
    此外,书中还给出了有限视野(实际上就是个半圆)和狭窄视野(实际上是一个扇形)的角度检测方法。在经过角度检测后,还要检测距离。如果向量d的数值小于当前单位的长度与视野半径的乘积,即可以认为目标单位与当前单位足够接近。这样经过角度检测和距离检测的单位,就是当前单位的邻近单位。
2.3 凝聚 

    凝聚考虑的是当前单位的速度向量v和相对位置向量u,其中u=邻近单位的平均位置-当前单位的位置。

if (DoFlock  &&  (N > 0 )) {  //  DoFlock=true:启用凝聚规则,N>0:邻近单位数量大于零
   Pave  =  Pave  /  N;  //  邻近单位的平均位置向量
   v  =  Units[i].vVelocity;  //  当前单位的速度向量
   v.Normalize(); 
   u 
=  Pave - Units[i].vPosition;  //  邻近单位平均位置向量与当前单位向量的差值,相对位置向量
   u.Normalize();
   w.VRotate2D(
- Units[i].fOrientation, u);
   
if (w.x  <   0 ) m  =   - 1 //  相对位置向量(即u)在当前单位的右边,需要右转当前单位
    if (w.x  >   0 ) m  =   1 ;   //  相对位置向量(即u)在当前单位的左边,需要左转当前单位
    if (fabs(v * u)  <   1 //  确保反余弦函数可以正常运行
     Fs.x  +=  m  *  _STEERINGFORCE  *  acos(v  *  u)  /  pi; 
     
//  Fs.x使用的坐标系是应该也是前面刚刚转化过的坐标系
     
//  acos(v*u):计算相对位置向量与当前单位的速度向量之间的夹角,除以pi是为了把弧度数值转化为标量
}

2.4 对齐

    对齐考虑的是当前单位的速度向量v和邻近单位的平均速度向量u,其他部分与凝聚部分的代码都一样,很好理解。

if (DoFlock  &&  (N > 0 )) {
    Vave 
=  Vave  /  N;
    u 
=  Vave;  //  邻近单位的平均速度向量
    u.Normalize();
    v 
=  Units[i].vVelocity;
    v.Normalize();
    w.VRotate2D(
- Units[i].fOrientation, u);
    
if (w.x  <   0 ) m  =   - 1 ;
    
if (w.x  >   0 ) m  =   1 ;
    
if (fabs(v * u)  <   1 )
         Fs.x 
+=  m  *  _STEERINGFORCE  *  acos(v  *  u)  /  pi;
}

        
                                                      图-邻近单位的平均位置和方向
2.5 分隔
    前面的凝聚和对齐规则,都会尝试让单位相互靠近一点。使用的分别是(速度向量,位置向量)和(速度向量,速度向量)。
        
                                                                   图-分隔
    外层弧线是可见视野,内层弧线是最小分隔距离。
    前面凝聚和对齐只需要计算平均位置,平均速度就可以了,但是对于分隔,需要当前单位与邻近的每个单位分别比较,对于进入最小间隔的单位,算出一个力,加到Fs.x上去。
    部分代码如下:

if (InView) {  //  如果在视野内
     if (d.Magnitude()  <=  Units[i].fLength  *  _SEPARATION_FACTOR) {
        
if (w.x  <   0 ) m  =   1 //  这里是分隔,方向与凝聚和对齐规则正好相反
         if (w.x  >   0 ) m  =   - 1 ;
        Fs.x 
+=  m  *  _STEERINGFOCE  *  (Units[i].fLength * _SEPARATION_FACTOR)  /  d.Magnitude();  //  分隔越小,力越大  
    }
}

3. 避开障碍物

    要做的就是提供某种机制给单位使用,让他们能看到前方的障碍物,再施加适当的转向力,是其避开路径中的障碍物。
    为了检测障碍物是否在某单位的路劲内,我们要借助机器人学,替我们的单位安装虚拟触角(feeler)。基本上,这些触角会处在单位的前方,如果触角碰到某种东西,就是那些单位要转向的时候了。模型的形式很多,比如可以装上三个触角,分别位于三个不同方向,不但能检测出是否有障碍物,而且检测该障碍物位于单位的那一侧。宽广的单位需要一个以上的触角,才能确保单位不会和障碍物碰撞。在3D游戏中,可以使用虚拟体积,以测定是否即将和某障碍物碰撞。总之,手段很多。
    这一节,给出了一个避开圆形障碍物的实例。具体代码忽略。总之,对当前单位做出的改变都会表现在Fs.x上面。
4. 跟随领头者 

    基本群聚算法的三条规则,似乎让群体在游戏世界中随处闲逛,如果在其中加入领头者,就能让群体的移动更有目的性,或者看起来比较有智能。比如:战争模拟游戏中,计算机控制一群飞机追击玩家。可以让其中的一架作为领头者,其他飞机采用基本群聚规则跟着领头者跑。在和玩家发生混战时,可以适时关闭群聚规则,让飞机分散进攻。另一个实例,仿真一直军队,指定其中某个单位为领头者,可以让他们成横队或者纵队,采用宽广视野或有限视野模式,使其他单位采取群聚行为。
   这一节给出的实例是通过某种规则确定领头者,这样即使领头者被玩家干掉了,或者由于某种原因脱离其群体,整个群体不会因此失去领导。
   一旦领头者确定了之后,就可以通过若干规则和技巧,让领头者做一些事情,比如,追逐某物,或者逃命。
   这一节给出的实例有点意思:把群体分成两类,一类单位的速度快一些,负责追击玩家,另一类速度慢一些,负责拦截玩家。两类分别找到各自的领头者,其他单位在自己的类别中,使用群聚算法。这样能够表现出,一组群体尾随在玩家之后,另一组群体从侧翼包抄的效果。
   具体领头者寻找的方法是通过计算每个单位窄视野范围内邻近单位的个数,如果要成为领头者,必然个数为0,如果有多个单位的窄视野内都没有单位,这一点书中没有交代,个人认为随机选取一个应该都可以吧。

你可能感兴趣的:(游戏开发)