[Silverlight动画]转向行为 - 追捕行为
对于追捕行为,它非常类似寻找行为。其实追捕的最后一个动作就是调用寻找。追捕的本质是预测目标所要到达的位置并事先赶到。
那么,该如何预测目标的位置呢?其实是以目标的当前速度不变为前提,算出未来一段时刻后目标所在的位置。但是需要计算多久的未来才合适呢?我们把这 段时间叫做预测时间(look ahead time)。如果算到很久以后(较长的预测时间)可能就会超越目标,如果只往后算一点点(较短的预测时间)可能仍然落后于目标。而实际上,寻找行为就是一个预测时间为零的追捕行为。
一个策略是基于两机车间的距离来判断预测时间。如果目标太远,需要花一段时间才能赶上,就预测长一点,如果很接近,马上就能达到,就预测短一点。把这个技术用于转向机车,就像这样:
public void pursue(Vehicle target) { double lookAheadTime = position.dist(target.position) / _maxSpeed; Vector2D predictedTarget = target.position.add(target.velocity.multiply(lookAheadTime)); seek(predictedTarget); }
首先通过两者间距离除以最大速率计算出预测时间。这就得到了追上目标所需要的时间间隔(假设目标不再移动)。通过目标的速度乘以时间间隔得到预测移动距离,再加到当前位置上就是预测位置。最后,把这个预测位置作为寻找点。
测试一下。这次要创建三辆机车。一个是只顾移动的机车,其作为目标,另外两个转向机车,一个用寻找行为,一个用追捕行为。如果一切正常,追捕者靠着优越的算法会胜出。
<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>
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(); } }
两辆转向机车开始于同一起点,但是寻找者向目标当前的位置移动,而追捕者则直接奔向目标的前方来拦截它。
对于这个测试,需要明确一点,有很多潜在的参数值会影响到结果。接下来的行为是:躲避。