到达行为在很多场合都可以被当作是寻找行为。实际上,它们之间的算法和处理方式都一样。唯一不同的是,在到达模式中,一辆机车在到达目标的某一距离时,会变成一种精确模式慢慢地靠近目标点。
为了了解到达行为的必要性,可以先运行一下SeekTest类,然后移动鼠标到某处让机车过来“抓住”它。会看到机车快速的越过了鼠标,接着它发现过头了,又返回来,还是过头了....于是会一直循环下去。这是因为机车始终保持着最大速度迈向目标,哪怕离目标只有几像素。
到达行为通过减速接近目标,解决了这个问题:
public void arrive(Vector2D target) {
Vector2D desiredVelocity = target.subtract(_postion);
desiredVelocity.normalize();
double dist = _postion.dist(target);
if (dist>_arrivalThreshold)
{
desiredVelocity = desiredVelocity.multiply(_maxSpeed);
}
else
{
desiredVelocity = desiredVelocity.multiply(_maxSpeed * dist / _arrivalThreshold);
}
Vector2D force = desiredVelocity.subtract(_velocity);
_steeringForce = _steeringForce.add(force);
}
程序一开始和寻找行为一样。但是在期望速度乘以最大速率时,做了距离检测。如果距离大于某个值,那一切照旧。程序往后走,接着的事情也和寻找一样。
关键是,距离小于某个值时所做的事情。本来乘以_maxSpeed现改为乘以_maxSpeed * dist / _arriveThreshold。如果距离仅仅小于某个值一点点,那么dist / _arriveThreshold会非常接近1.0,可能是0.99。因此,期望速度的大小也会非常接近于(略小于)最大速率。如果距离接近0,那么得到 的比率也会非常非常小,期望速度改变也会很小。最终速度会趋向于0(假设只有一个行为作用于该机车)。
当然,转向机车类需要这么一个“某个值”属性,所以我们把它加上去:
private double _arrivalThreshold = 100;
public double arriveThreshold {
get { return _arrivalThreshold; }
set { _arrivalThreshold = value; }
}
看看这些是如何运用在测试类中的:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Steer" xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
mc:Ignorable="d"
x:Class="Steer.ArriveTest"
d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot" Background="White">
<local:SteeredVehicle x:Name="myStar" HorizontalAlignment="Left" Height="40" VerticalAlignment="Top" Width="40">
<ed:RegularPolygon Fill="Blue" Height="40" InnerRadius="1" PointCount="3" Stretch="Fill" Stroke="Black" UseLayoutRounding="False" Width="40" RenderTransformOrigin="0.5,0.5" StrokeThickness="0">
<ed:RegularPolygon.RenderTransform>
<CompositeTransform Rotation="90"/>
</ed:RegularPolygon.RenderTransform>
</ed:RegularPolygon>
</local:SteeredVehicle>
</Grid>
</UserControl>
public partial class ArriveTest : UserControl
{
double mouseX = 0;
double mouseY = 0;
public ArriveTest()
{
// Required to initialize variables
InitializeComponent();
Loaded += new RoutedEventHandler(SeekTest_Loaded);
}
void SeekTest_Loaded(object sender, RoutedEventArgs e)
{
MouseMove += new MouseEventHandler(SeekTest_MouseMove);
CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
}
void CompositionTarget_Rendering(object sender, EventArgs e)
{
myStar.arrive(new Vector2D(mouseX, mouseY));
myStar.update();
}
void SeekTest_MouseMove(object sender, MouseEventArgs e)
{
mouseX = e.GetPosition(null).X;
mouseY = e.GetPosition(null).Y;
}
}
和测试寻找行为唯一的不同就是在Rendering中把函数名seek换成了arrive。运行一下试试把鼠标移动到某处,机车先是以寻找模式发现 目标,然后慢慢的停在鼠标所在位置。再次移动鼠标又会回到寻找模式。通过调整arriveThreshold属性,看看机车接近目标时的变化吧。
如果愿意可以再试着玩玩增加多辆机车,或者现在就进入下一个行为:追捕。