避开行为与寻找行为彻底相反。实际上,除了代码最后一行用相减代替了相加以外,其它都一样。
public void flee(Vector2D target) { Vector2D desiredVelocity = target.subtract(_postion); desiredVelocity.normalize(); desiredVelocity = desiredVelocity.multiply(_maxSpeed); Vector2D force = desiredVelocity.subtract(_velocity); _steeringForce = _steeringForce.subtract(force); }
由于和寻找几乎一样,所以就不详细解释了。最后一句的含义是“很好,既然发现了目标,那就调头往回走吧。”在此为避开做一个简单的测试:
<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.FleeTest" 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" RenderTransformOrigin="0.5,0.5"> <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 FleeTest : UserControl { double mouseX = 0; double mouseY = 0; public FleeTest() { // Required to initialize variables InitializeComponent(); Loaded += new RoutedEventHandler(SeekTest_Loaded); } void SeekTest_Loaded(object sender, RoutedEventArgs e) { myStar.position = new Vector2D(100, 100); myStar.edgeBehavior = Vehicle.BOUNCE; myStar.velocity.length = 5; myStar.velocity.angle = Math.PI / 4; MouseMove += new MouseEventHandler(SeekTest_MouseMove); CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering); } void CompositionTarget_Rendering(object sender, EventArgs e) { myStar.flee(new Vector2D(mouseX, mouseY)); myStar.update(); } void SeekTest_MouseMove(object sender, MouseEventArgs e) { mouseX = e.GetPosition(null).X; mouseY = e.GetPosition(null).Y; } }
抛开代码中调用的函数名称不谈,最主要的不同是,用两行代码将机车的初始位置放在离场景边缘有一段距离的地方,并且把接触场景边缘后的反应改为反弹。删除这两行再测试,就知道为什么要加这两行了。机车会为了避开鼠标在角落间来回切换,导致近似看不见。
现在我们有了一对正反行为,接下来要做的是为这对行为创建一对机车来看看情况。
<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.SeekFleeTest1" d:DesignWidth="640" d:DesignHeight="480"> <Grid x:Name="LayoutRoot" Background="White"> <local:SteeredVehicle x:Name="mySeek" HorizontalAlignment="Left" Height="40" VerticalAlignment="Top" Width="40" RenderTransformOrigin="0.5,0.5" Margin="84,50,0,0"> <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="myflee" HorizontalAlignment="Left" Height="40" VerticalAlignment="Top" Width="40" RenderTransformOrigin="0.5,0.5"> <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 SeekFleeTest1 : UserControl { public SeekFleeTest1() { // Required to initialize variables InitializeComponent(); Loaded += new RoutedEventHandler(SeekTest_Loaded); } void SeekTest_Loaded(object sender, RoutedEventArgs e) { mySeek.position = new Vector2D(200, 200); mySeek.edgeBehavior = Vehicle.BOUNCE; myflee.position = new Vector2D(100, 300); myflee.edgeBehavior = Vehicle.BOUNCE; CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering); } void CompositionTarget_Rendering(object sender, EventArgs e) { mySeek.seek(myflee.position); myflee.flee(mySeek.position); mySeek.update(); myflee.update(); } }
这对机车叫寻找者(_seeker)和避开者(_fleer)。我相信不用再解释寻找者寻找避开者,避开者避开寻找者了吧。先运行一下看看两辆机车互动的效果,然后尝试改变它们的各种参数,再看看会发生什么。
我们还可以把两个行为同时用于一辆机车上。下面的例子中,机车A同时寻找和避开机车B,机车B同时寻找和避开机车C,机车C同时的寻找和避开机车A。这三辆机车会因为追捕各自的目标而形成一个圆。
<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.SeekFleeTest2" d:DesignWidth="640" d:DesignHeight="480"> <Grid x:Name="LayoutRoot"> <local:SteeredVehicle x:Name="myA" HorizontalAlignment="Left" Height="40" VerticalAlignment="Top" Width="40" RenderTransformOrigin="0.5,0.5"> <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="myB" HorizontalAlignment="Left" Height="40" VerticalAlignment="Top" Width="40" RenderTransformOrigin="0.5,0.5"> <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="myC" HorizontalAlignment="Left" Height="40" VerticalAlignment="Top" Width="40" RenderTransformOrigin="0.5,0.5"> <ed:RegularPolygon Fill="#FFCEFF00" 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 SeekFleeTest2 : UserControl { public SeekFleeTest2() { // Required to initialize variables InitializeComponent(); Loaded += new RoutedEventHandler(SeekTest_Loaded); } void SeekTest_Loaded(object sender, RoutedEventArgs e) { myA.position = new Vector2D(200, 200); myA.edgeBehavior = Vehicle.BOUNCE; myB.position = new Vector2D(400, 200); myB.edgeBehavior = Vehicle.BOUNCE; myC.position = new Vector2D(300, 260); myC.edgeBehavior = Vehicle.BOUNCE; CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering); } void CompositionTarget_Rendering(object sender, EventArgs e) { myA.seek(myB.position); myA.flee(myC.position); myB.seek(myC.position); myB.flee(myA.position); myC.seek(myA.position); myC.flee(myB.position); myA.update(); myB.update(); myC.update(); } }
记得再试试改变各种参数,观察发生的变化。如果这些都没问题了,那就开始探索下一个行为:到达。