避开行为与寻找行为彻底相反。实际上,除了代码最后一行用相减代替了相加以外,其它都一样。
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();
}
}
记得再试试改变各种参数,观察发生的变化。如果这些都没问题了,那就开始探索下一个行为:到达。