Steering Behaviors: Wander 漫游

游戏中的个体常常会有随机性的巡逻情景。通常这些个体在等待某些事情发生,比如说发现玩家然后开战,或者寻找某些东西。当这些行为呈现在玩家面前时,它们必须在视觉感官上市真实可信,富有乐趣的。

如果玩家能够很容易的分辨出游戏AI的运动路径,或者别的不真实的移动行为,这会增加玩家的挫败感。最差的情况,玩家可以很清楚的预判AI的移动行为,这最终会导致一个枯燥的游戏体验。

Wander 行为意图产生一种真实可玩的移动行为,使玩家以为游戏AI是真实的生命体在游戏中巡逻。

基于SteeringBehaviors 有几种方法来实现Wander的特征。最简单的是使用前面提到过的Seek行为,在游戏AI进行Seek行为的时候,它会朝着目标不断前进,如果目标点每隔几秒钟改变一次,那么游戏AI就永远也到不了目标点,在游戏场景中不断的改变目标点,会让游戏AI不断的去追踪目标。

最简单实现漫游模式的方法是利用seek行为。

如果目标每隔几秒就改变,角色会永远到达不了目标,就算到达也会再次移动。随机改变target目标来模拟漫游效果。

// Calculate the wander force
private function wander() :Vector3D {
   var now :Number = (new Date()).getTime();

   if (now >= nextDecision) {
    // Choose a random position for "target"
   }

   // return a steering force that pushes the character
   // towards the target (the seek behavior)
   return seek(target);
}

// In the game loop, handle forces and movement just
// like before:
public function update() :void {
   steering = wander()
   steering = truncate (steering, max_force)
   steering = steering / mass
   velocity = truncate (velocity + steering , max_speed)
   position = position + velocity
}

尽管这是一个不错的解决方法,但是最终的结果并不是那么让人信服。有时候游戏AI会完全翻转他们的行动路径,因为目标点正好随机到了身后。游戏AI的行为看上去更像是“哎呀,我忘记了我的钥匙”然后是“好吧,我就顺着这个方向走”。

另一种实现方法是由 Craig W. Reynolds 提出的。

实现方式是每帧产生一个 小而随机的 移位力来作用在游戏AI当前移动方向之上。因为速度向量代表了个体移动的方向,所以任何微小的改变会影响到个体的行进方向。

每一帧施加一个微小的 移位力会规避游戏AI突然改变自己路径的突兀感,比如游戏AI上一帧是朝上方运动并朝右转向,那这一帧该游戏对象同样是朝上方运动并朝右转向,区别只是角度有一点不同。

有很多种方式实现这种思路,起中一种简单的方式是在游戏AI的前方加一个圈,圈的半径以及角色到圈的距离越大,施加在角色身上的推力就越强。

Wander behavior

移位力(displacement)有自己的圆心,并且会被半径影响。圆心越大,推力越大。

这个移位力会用于计算漫游力。

要计算Wander力,第一步是要得出圈的中心位置,因为圈必须是在角色的正前方,所以我们可以利用速度向量作为方向。

// The CIRCLE_DISTANCE constant below is
// a number defined somewhere else.
// The code to calculate the circle center:
var circleCenter :Vector3D;
circleCenter = velocity.clone();
circleCenter.normalize();
circleCenter.scaleBy(CIRCLE_DISTANCE);

 

上面的circleCenter向量是速度向量的拷贝,它被归一化后乘以了一个放大系数。

Calculating circle's position

下一步是计算移位力,它负责使个体左转和右转。因为这是一个用于扰乱的力,所以它其实可以指向任意方向,我们暂且用一个和Y轴平行的力来表示。

var displacement :Vector3D;
displacement = new Vector3D(0, -1);
displacement.scaleBy(CIRCLE_RADIUS);
//
// Randomly change the vector direction
// by making it change its current angle
setAngle(displacement, wanderAngle);
//
// Change wanderAngle just a bit, so it
// won't have the same value in the
// next game frame.
wanderAngle += (Math.random() * ANGLE_CHANGE) - (ANGLE_CHANGE * .5);

移位力被创建出来,并且根据圈的半径被放大,和前面提到的一样,圈的半径越大,移位力就越大,wadnerAngle是一个缩放因子,它定义了移位力该倾斜多少,这里使用了一个随机值来让它每帧都不一样。为了更好的理解原理,我们假设移位力是在圈的中心计算的。因为它的向量长度等于圈的半径,所以它看起来如同这样:

在计算完圈的中心和,移位力的大小之后,将他们整合在一起就是Wander力。

Displacement force
提示:记得数学向量在空间没有坐标,他们有方向和长度。可以摆放到任意位置。

计算公式:

var wanderForce :Vector3D;
wanderForce = circleCenter.add(displacement);

从视觉上来看,它应该是这样:

Adding forces

wander力可以想象成以游戏AI为起点,指向圈上的某个点向量,具体这个点的位置会决定施加在游戏AI身上的力是朝左还是右,是强还是弱:

Analyzing the wander force

wander力越和速度向量平行,游戏AI转变得就越少,wander力和之前介绍的seek和flee力一样,会将游戏AI推向一个方向。但是不同的是前者是根据圈上的一个随机位置来决定推向哪,而后者是根据一个目标位置。

最终计算漫游力代码如下:

private function wander() :Vector3D {
   var circleCenter :Vector3D;
   circleCenter = velocity.clone();
   circleCenter.normalize();
   circleCenter.scaleBy(CIRCLE_DISTANCE);
   var displacement :Vector3D;
   displacement = new Vector3D(0, -1);
   displacement.scaleBy(CIRCLE_RADIUS);
   setAngle(displacement, wanderAngle);
   wanderAngle += Math.random() * ANGLE_CHANGE - ANGLE_CHANGE * .5;
  var wanderForce :Vector3D;
   wanderForce = circleCenter.add(displacement);
   return wanderForce;
}

public function setAngle(vector :Vector3D, value:Number):void {
   var len :Number = vector.length;
   vector.x = Math.cos(value) * len;
   vector.y = Math.sin(value) * len;
}

当计算完漫游力,增加到角色的速度从二影响移动。代码如下

steering = wander()
steering = truncate (steering, max_force)
steering = steering / mass
velocity = truncate (velocity + steering , max_speed)
position = position + velocity

发表评论

邮箱地址不会被公开。