[Silverlight动画]转向行为 - 机车
机车类是转向角色的基类,但它不提供任何转向行为,只处理与运动相关的基本内容,如位置,速度,质量以 及角色接触场景边缘后的反应(反弹还是穿越出现在另一边)。转向机车(SteeredVehicle)类继承机车类,并为之增加转向行为。使用这样的结构 其目的是为了让机车类可以用于仅需要移动而不需要转向行为的对象,同时也可以让转向机车类不考虑基本运动的细节而专心实现转向功能。
/// <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类做一个快速测试
<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>
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函数。
机车类的测试已经足够了。让我们开始迈向更好更强大的转向行为之旅吧。
- LegacyText的复制的Bug
- 提高WCF服务并发能力的简单处理办法
- 如何给已经有数据的DataTable动态增加一列并赋值
- Gitlab上采用rpm方式快速安装的操作记录
- WCF与IIS集成Windows身份验证的矛盾
- openstack中彻底删除计算节点的操作记录
- 验证码无刷新更换
- 从MapX到MapXtreme2004[12]-SearchNearest!
- Sublime Text的安装与配置
- 复制到浮动层,强调显示某个(多个)区域
- Jquery实现的一种常用高亮效果
- MaCfee导致Asp.net/Jmail无法发送邮件的解决办法
- 水晶报表文本在web中无法两端对齐
- jenkins配置记录(2)--代码发布流程
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- xshell 远程登陆CentOS7 免密登陆的思路详解
- Linux服务器下Nginx与Apache共存的实现方法分析
- 浅析Linux中vsftpd服务配置(匿名,用户,虚拟用户)
- CentOS7开启MySQL8主从备份、每日定时全量备份(推荐)
- leetcode栈之用两个栈实现队列
- Ubuntu 18.04通过命令禁用/开启触控板
- 如何利用watch帮你重复执行命令
- Linux查看当前登录用户并踢出用户的命令
- centos7下安装java及环境变量配置技巧
- Ubuntu系统下扩展LVM根目录的方法
- 将宝塔面板linux版装在/www以外的目录的方法
- 详解在Linux下9个有用的touch命令示例
- 一步步教你如何开启、关闭ubuntu防火墙
- Linux环境ActiveMQ部署方法详解
- Ubuntu 16.04无法切换root权限问题的解决