漫游的目的
漫游是指角色在场景中毫无目的的移动
.这通常用来模拟巡视和觅食,
也有纯粹是为了漫游而漫游的.
漫游行为
简单的使用随机而产生的布朗运动,会让角色感觉像一个神经病的傻瓜.
我们需要更自然更平滑的感觉.
有个办法,通常设想在角色前方有个圆,然后把圆上任意一点作为目标,每次更新都想这个随机点移动.
由于目标点总是落在假象的圆上, 所以转向力永远不会一下子就变化很大.
可以参照右图加以理解!
交通工具的前端凸出个圆圈,目标被限制在该圆圈上,然后我们移向目标。每帧给目标添加一个随机的位置,随着时间的推移,沿着圆周移来移去,以创建一个没有抖动的往复运动。
利用不同的圆圈尺寸、到交通工具的距离、每帧的随机位移的大小,这个方法可以产生所有范围的往复运动,从非常光滑的波状式转弯到狂野的Strictly Ballroom式旋转,再到以脚尖立地的旋转。
目标
如<AdvancED
ActionScript3>的描述,实现机车的漫游行为.
实现
l创建圆
l在圆上添加随机点,引导机车转向
l向圆上的随机点转向
讨论
l创建圆
在机车的正前方添加一个圆.要添加圆,就一定要有圆心.
这个圆心处在机车的正前方,也就是说要在velocity的延长线上,
通过上面的图解也能看的出来,那这个圆心离机车多远呢?我们定义这个距离为
_wanderDistance:
private var _wanderDistance:Number = 50; //机车漫游的圆心位置
public function set wanderDistance(value):void {
_wanderDistance = value;
}
public function get wanderDistance():Number {
return _wanderDistance;
}
然后是圆心的计算:
var center:Vector2D = _velocity.clone().normalize().multiply(_wanderDistance);
//在机车的正前方_wanderDistance位置,添加圆的圆心
再多啰嗦两句,velocity.clone().normalize()的作用是将velocity单位化,因为我们要的只是velocity的方向,
然后在velocity的方向上乘以_wanderDistance就是圆心的位置了!
另外,圆心的位置是实时更新的,所以上面的代码在wander方法中,要和update方法一起更新的.
圆心定下来了,下面是圆的半径:
private var _wanderRadius:Number = 30; //机车漫游目标的范围
public function set wanderRadius(value):void {
_wanderRadius = value;
}
public function get wanderRadius():Number {
return _wanderRadius;
}
l在圆上添加随机点,引导机车转向
这样,图中的那个圆我们就”画出来了”,然后,就是在圆上添加随机的点,引导机车转向.
所谓随机是相对于圆心来讲的.所以随机点的位置可以用
圆心矢量center 加上偏移矢量offset,
为了让随机点落在圆上,
我们就把偏移量offset的长度限定为半径wanderRadius.
然后随机化偏移量offset的角度就好了,代码如下:
var offset:Vector2D = new Vector2D(0); //随机点与圆心的偏移量
offset.length = _wanderRadius; //为是随机点落在圆心上,限定偏移量的长度为_wanderRadius
offset.angle = _wanderAngle; //随机偏移的角度
_wanderAngle += (Math.random()-0.5);
var force:Vector2D = center.add(offset);//圆心矢量加偏移矢量确定随机点,随机点确定后,直接添加随机点方向的转向力.
l向圆上的随机点转向
随机点确定后,直接添加随机点方向的转向力force,然后这个转向力force与之前的steerForce叠加即是新的作用力:
steerForce = _steerForce.add(force);
//讲计算出来的转向力与原转向力叠加
public function wander():void {
var center:Vector2D = _velocity.clone().normalize().multiply(_wanderDistance);
//在机车的正前方_wanderDistance位置,添加圆的圆心
var offset:Vector2D = new Vector2D(0);
//随机点与圆心的偏移量
offset.length = _wanderRadius;
//为是随机点落在圆心上,限定偏移量的长度为_wanderRadius
offset.angle = _wanderAngle;
//随机偏移的角度
_wanderAngle += (Math.random()-0.5);
var force:Vector2D = center.add(offset);
//圆心矢量加偏移矢量确定随机点,随机点确定后,直接添加随机点方向的转向力.
steerForce = _steerForce.add(force);
//讲计算出来的转向力与原转向力叠加
}
public class Wander extends Sprite
{
private var wanderVehicle:Vehicle; //漫游机车实例
public function Wander()
{
wanderVehicle = new Vehicle();
wanderVehicle.position = new Vector2D(100, 200);
addChild(wanderVehicle);
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
//按照帧频随速度更新机车的位置
private function enterFrameHandler(e:Event):void {
wanderVehicle.wander();
wanderVehicle.update(); //如下代码
}
}
wanderVehicle.update 的代码:
重点:
velocity = _velocity.add(_steerForce); //速度与转向力的矢量叠加
_position = _position.add(_velocity); //位置与速度的矢量叠加
public function update():void {
velocity = _velocity.add(_steerForce); //速度与转向力的矢量叠加
_position = _position.add(_velocity); //位置与速度的矢量叠加
this.x = _position.x;
this.y = _position.y;
//判断并设置边缘行为
if (_edgeBehavior == "bounce") {
bounce();
}else if (_edgeBehavior == "wrap") {
wrap();
}
this.rotation = _velocity.angle * 180 / Math.PI;
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
C++ 代码:
//--------------------------- Wander -------------------------------------
//
// This behavior makes the agent wander about randomly
//------------------------------------------------------------------------
Vector2D SteeringBehavior::Wander()
{
//<span style="font-family: 'Times New Roman';">每秒加到目标的随机位移的最大值 * 记录最近更新的时间</span>
double JitterThisTimeSlice = m_dWanderJitter * m_pVehicle->TimeElapsed();
//<span style="font-family: 'Times New Roman';">首先,加一个小的随机向量到目标位置</span>
<span style="font-family:Times New Roman;"> //</span><span style="font-family: 'Times New Roman';">RandomClamped 返回-1到1之间的一个数</span><span style="font-family:Times New Roman;">
m_vWanderTarget += Vector2D(RandomClamped() * JitterThisTimeSlice,
RandomClamped() * JitterThisTimeSlice);
//把这个新的向量重新投影回单元圆周上</span>
<span style="font-family:Times New Roman;"> //先标准化,确定方向,再乘以半径回到圆上了。
m_vWanderTarget.Normalize();
m_vWanderTarget *= m_dWanderRadius;
//上面两行代码表示:先标准化再乘以半径,标准化只表示方向,再乘以半径表示加上大小
//移动目标位置在智能体前面WanderDist位置。
Vector2D target = m_vWanderTarget + Vector2D(m_dWanderDistance, 0);
//吧目标投影到世界空间
Vector2D Target = PointToWorldSpace(target,
m_pVehicle->Heading(),
m_pVehicle->Side(),
m_pVehicle->Pos());
//移向它,操控力
return Target - m_pVehicle->Pos();
}</span>