对于追捕行为,它非常类似寻找行为。其实追捕的最后一个动作就是调用寻找。追捕的本质是预测目标所要到达的位置并事先赶到。
那么,该如何预测目标的位置呢?其实是以目标的当前速度不变为前提,算出未来一段时刻后目标所在的位置。但是需要计算多久的未来才合适呢?我们把这 段时间叫做预测时间(look ahead time)。如果算到很久以后(较长的预测时间)可能就会超越目标,如果只往后算一点点(较短的预测时间)可能仍然落后于目标。而实际上,寻找行为就是一个预测时间为零的追捕行为。
一个策略是基于两机车间的距离来判断预测时间。如果目标太远,需要花一段时间才能赶上,就预测长一点,如果很接近,马上就能达到,就预测短一点。把这个技术用于转向机车,就像这样:
代码语言:javascript复制 public void pursue(Vehicle target) {
double lookAheadTime = position.dist(target.position) / _maxSpeed;
Vector2D predictedTarget = target.position.add(target.velocity.multiply(lookAheadTime));
seek(predictedTarget);
}
首先通过两者间距离除以最大速率计算出预测时间。这就得到了追上目标所需要的时间间隔(假设目标不再移动)。通过目标的速度乘以时间间隔得到预测移动距离,再加到当前位置上就是预测位置。最后,把这个预测位置作为寻找点。
测试一下。这次要创建三辆机车。一个是只顾移动的机车,其作为目标,另外两个转向机车,一个用寻找行为,一个用追捕行为。如果一切正常,追捕者靠着优越的算法会胜出。
代码语言:javascript复制<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" x:Class="Steer.PursueTest"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<local:SteeredVehicle x:Name="mySeeker" HorizontalAlignment="Left" Height="40" VerticalAlignment="Top" Width="40" RenderTransformOrigin="0.5,0.5" d:IsHidden="True">
<ed:RegularPolygon Fill="#FFFF0C00" 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>
<local:SteeredVehicle x:Name="myPursuer" HorizontalAlignment="Left" Height="40" VerticalAlignment="Top" Width="40" RenderTransformOrigin="0.5,0.5" d:IsHidden="True">
<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>
<local:SteeredVehicle x:Name="myTarget" HorizontalAlignment="Left" Height="40" VerticalAlignment="Top" Width="40" RenderTransformOrigin="0.5,0.5">
<ed:RegularPolygon Fill="#FFFD00FF" Height="40" InnerRadius="1" PointCount="5" Stretch="Fill" Stroke="Black" StrokeThickness="0" UseLayoutRounding="False" Width="40"/>
</local:SteeredVehicle>
</Grid>
</UserControl>
代码语言:javascript复制 public partial class PursueTest : UserControl
{
public PursueTest()
{
InitializeComponent();
Loaded = new RoutedEventHandler(PursueTest_Loaded);
}
void PursueTest_Loaded(object sender, RoutedEventArgs e)
{
mySeeker.x = 400;
myPursuer.x = 400;
myTarget.position = new Vector2D(200, 100);
myTarget.velocity.length = 15;
myTarget.velocity.angle = Math.PI / 4;
CompositionTarget.Rendering = new EventHandler(CompositionTarget_Rendering);
}
void CompositionTarget_Rendering(object sender, EventArgs e)
{
mySeeker.seek(myTarget.position);
mySeeker.update();
myPursuer.pursue(myTarget);
myPursuer.update();
myTarget.update();
}
}
两辆转向机车开始于同一起点,但是寻找者向目标当前的位置移动,而追捕者则直接奔向目标的前方来拦截它。
对于这个测试,需要明确一点,有很多潜在的参数值会影响到结果。接下来的行为是:躲避。