[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属性,看看机车接近目标时的变化吧。
如果愿意可以再试着玩玩增加多辆机车,或者现在就进入下一个行为:追捕。 

posted @ 2010-06-24 19:18  王喆(nasa)  阅读(2087)  评论(1编辑  收藏  举报