[Silverlight动画]转向行为 - 到达行为
到达行为在很多场合都可以被当作是寻找行为。实际上,它们之间的算法和处理方式都一样。唯一不同的是,在到达模式中,一辆机车在到达目标的某一距离时,会变成一种精确模式慢慢地靠近目标点。
为了了解到达行为的必要性,可以先运行一下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属性,看看机车接近目标时的变化吧。
如果愿意可以再试着玩玩增加多辆机车,或者现在就进入下一个行为:追捕。