[Silverlight动画]转向行为 - 机车

2018-01-16 11:57:29 浏览数 (1)

机车类是转向角色的基类,但它不提供任何转向行为,只处理与运动相关的基本内容,如位置,速度,质量以 及角色接触场景边缘后的反应(反弹还是穿越出现在另一边)。转向机车(SteeredVehicle)类继承机车类,并为之增加转向行为。使用这样的结构 其目的是为了让机车类可以用于仅需要移动而不需要转向行为的对象,同时也可以让转向机车类不考虑基本运动的细节而专心实现转向功能。

代码语言:javascript复制
    /// <summary>
    /// 机车类
    /// </summary>
    public class Vehicle : Canvas
    {
        protected string _edgeBehavior = WRAP;
        protected double _mass = 1.0;
        protected double _maxSpeed = 10;
        protected Vector2D _postion;
        protected Vector2D _velocity;

        private CompositeTransform _compositeTransform;
        private TransformGroup _transformGroup;

        /// <summary>
        /// 边缘行为
        /// </summary>
        public const string WRAP = "wrap";
        public const string BOUNCE = "bounce";

        public Vehicle()
        {
            _postion = new Vector2D(0, 0);
            _velocity = new Vector2D(0, 0);
            this.Loaded  = new System.Windows.RoutedEventHandler(Vehicle_Loaded);
        }

        void Vehicle_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
            var transformGroup = this.RenderTransform as TransformGroup;
            if (transformGroup == null)
            {
                _transformGroup = new TransformGroup();
                this.RenderTransform = _transformGroup;
                _compositeTransform = new CompositeTransform();
                _transformGroup.Children.Add(_compositeTransform);
            }
        }

        public virtual void update()
        {
            _velocity.truncate(_maxSpeed);
            _postion = _postion.add(_velocity);

            if (_edgeBehavior == WRAP)
            {
                wrap();
            }
            else if (_edgeBehavior == BOUNCE)
            {
                bounce();
            }

            x = position.x;
            y = position.y;
            _compositeTransform.Rotation = _velocity.angle * 180 / Math.PI;
        }

        /// <summary>
        /// 反弹
        /// </summary>
        private void bounce()
        {
            Content stage = App.Current.Host.Content;
            if (position.x > stage.ActualWidth)
            {
                position.x = stage.ActualWidth;
                velocity.x *= -1;
            }
            else if (_postion.x < 0)
            {
                position.x = 0;
                velocity.x *= -1;
            }

            if (position.y > stage.ActualHeight)
            {
                position.y = stage.ActualHeight;
                velocity.y *= -1;
            }
            else if (position.y < 0)
            {
                position.y = 0;
                velocity.y *= -1;
            }
        }

        /// <summary>
        /// 巡游
        /// </summary>
        private void wrap()
        {
            Content stage = App.Current.Host.Content;
            if (position.x > stage.ActualWidth)
            {
                position.x = 0;
            }
            if (position.x < 0)
            {
                position.x = stage.ActualWidth;
            }
            if (position.y > stage.ActualHeight)
            {
                position.y = 0;
            }
            if (position.y < 0)
            {
                position.y = stage.ActualHeight;
            }
        }

        public string edgeBehavior
        {
            get
            {
                return _edgeBehavior;
            }
            set
            {
                _edgeBehavior = value;
            }
        }

        public double mass
        {
            get
            {
                return _mass;
            }
            set
            {
                _mass = value;
            }
        }

        public double maxSpeed
        {
            get
            {
                return _maxSpeed;
            }
            set
            {
                _maxSpeed = value;
            }
        }

        public Vector2D position
        {
            get
            {
                return _postion;
            }
            set
            {
                _postion = value;
                x = _postion.x;
                y = _postion.y;
            }
        }

        public Vector2D velocity
        {
            get
            {
                return _velocity;
            }
            set
            {
                _velocity = value;
            }
        }

        public double x
        {
            get
            {
                return _compositeTransform.TranslateX;
            }
            set
            {
                _compositeTransform.TranslateX = value;
                _postion.x = value;
            }
        }

        public double y
        {
            get
            {
                return _compositeTransform.TranslateY;
            }
            set
            {
                _compositeTransform.TranslateY = value;
                _postion.y = value;
            }
        }
    }

首先,采用两个2D向量来分别表示位置和速度,用_position,_velocity代替x,y,vx,vy。 大多数工作都发生在update函数中。一上来先试着截断(truncate)速度向量,确保不会超过最大速度,然后把速度向量加于(add)位置向量上。

_position = _position.add(_velocity);

接着检测是否在边缘,是的话调用wrap或者bounce函数。最终,根据位置向量更新对象的x和y值,并调整其角度:

  x = position.x;

  y = position.y;

  _compositeTransform.Rotation = _velocity.angle * 180 / Math.PI;

为Vehicle类做一个快速测试

代码语言:javascript复制
<UserControl x:Class="Steer.VehicleTest"
    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"
    d:DesignHeight="300" d:DesignWidth="400">
    
    <Grid x:Name="LayoutRoot" Background="White">
        <local:Vehicle x:Name="myStar" HorizontalAlignment="Left" Width="40" Height="40" VerticalAlignment="Top" Margin="0" RenderTransformOrigin="0.5,0.5">
        	<ed:BlockArrow Fill="#FFF4F4F5" Height="40" Orientation="Right" Stroke="Black" UseLayoutRounding="False" Width="40"/>
        </local:Vehicle>
    </Grid>
</UserControl>
代码语言:javascript复制
    public partial class VehicleTest : UserControl
    {
        public VehicleTest()
        {
            InitializeComponent();

            this.Loaded  = new RoutedEventHandler(MainPage_Loaded);
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            CompositionTarget.Rendering  = new EventHandler(CompositionTarget_Rendering);
            myStar.edgeBehavior = SteeredVehicle.BOUNCE;
            myStar.position = new Vector2D(100, 100);
            myStar.velocity.length = 5;
            myStar.velocity.angle = Math.PI / 4;
        }

        void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            myStar.update();
        }
    }

Vehicle对象并增加到显示列表。它的位置由一个2D向量决定:

_vehicle.position = new Vector2D(100, 100);

在例子中将Vehicle对象放到舞台中。它的位置由一个2D向量决定:_vehicle.position = new Vector2D(100, 100);

另一个改变位置的方法是直接设置位置的x和y值。

myStar.position.x = 100; 

myStar.position.y = 100;

或者直接设置被重载过的x和y,与此同时position也会跟着一起改变。

myStar.x = 100; 

myStar.y = 100;

例子中对设置速度采用了另一种方式:长度(length)和角度(angle),这也显示了向量在使用上的弹性。

myStar.velocity.length = 5;

myStar.velocity.angle = Math.PI / 4;

长度在这里指速度的大小,角度指方向。angle是弧度,所以Math.PI / 4相当于45度。

最后在CompositionTarget.Rendering事件上调用update函数。

机车类的测试已经足够了。让我们开始迈向更好更强大的转向行为之旅吧。

0 人点赞