Silverlight动画基础三:动画与向量-模拟重力效果

这次将使用向量和动画结合来模拟物体在具有重力下的运动效果,主要逻辑:

1.定义小球的重力,摩擦力、以及运动速度的递减变量

2.根据鼠标拖拽小球的前后位置的差值来设置小球的起始速度,拖动越快速度也就越大。

3.小球开始运动的时候 ,首先根据小球的重力从新设置小球在Y轴上的运动速度,其次检测小球在运动到上下左右边界的情况,以及运动到角

落的情况,并重新设置小球位置。

运行效果图如下:

image

一、ball对象

主要逻辑均有ball对象处理,其代码如下:

ball.xaml 代码

<UserControl.Resources>
<Storyboard x:Name="Move" Duration="00:00:00"/>
</UserControl.Resources>
<Canvas x:Name="LayoutRoot" Width="40" Height="40">
<Ellipse Width="40" Height="40" Canvas.Left="0" Canvas.Top="0" x:Name="redEllipse">
<Ellipse.Fill>
<RadialGradientBrush>
<RadialGradientBrush.RelativeTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1"/>
<SkewTransform AngleX="0" AngleY="0"/>
<RotateTransform Angle="0"/>
<TranslateTransform X="0.15" Y="-0.125"/>
</TransformGroup>
</RadialGradientBrush.RelativeTransform>
<GradientStop Color="#FFFFFFFF" Offset="0"/>
<GradientStop Color="#FFFF0000" Offset="1"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Canvas>

 

ball.xaml.cs代码

public partial class ball : UserControl
{
/// <summary>
/// 重力
/// </summary>
public double Gravity;

/// <summary>
/// 容器高度
/// </summary>
public double RootHeight;

/// <summary>
/// 容器宽度
/// </summary>
public double RootWidth;

/// <summary>
/// 是否捕捉焦点
/// </summary>
private bool isMouseCaptured;

/// <summary>
/// 鼠标位置
/// </summary>
private Point mousePosition;

/// <summary>
/// 鼠标位置缓存
/// </summary>
private Point oldMouse;

/// <summary>
/// 小球位置
/// </summary>
private Point ballPosition;

/// <summary>
/// 小球X轴速度
/// </summary>
private double VelocityX = 0;
/// <summary>
/// 小球Y轴速度
/// </summary>
private double VelocityY = 0;

//偏移量,X/Y轴速度均要乘以此变量
private double restitution = .6;
//摩擦力
private double friction = .9;

public ball()
{
InitializeComponent();
this.MouseLeftButtonDown += new MouseButtonEventHandler(ball1_MouseLeftButtonDown);
this.MouseLeftButtonUp += new MouseButtonEventHandler(ball1_MouseLeftButtonUp);
this.MouseMove += new MouseEventHandler(ball1_MouseMove);
Move.Completed += new EventHandler(Move_Completed);

}

void Move_Completed(object sender, EventArgs e)
{
//根据重力计算小球速度,重力影响Y轴速度
VelocityY += Gravity;

//根据小球上一次的位置参数、小球的速度和方向更新ballPosition的值
ballPosition.X += VelocityX;
ballPosition.Y += VelocityY;

//考虑小球运动到角落的情况
if (ballPosition.Y + this.Height >= RootHeight && (ballPosition.X + this.Width) >= RootWidth)
{
//小球运动到右下角
ballPosition.X = RootWidth - (this.Width + 1);
}

if (ballPosition.Y + this.Height >= RootHeight && ballPosition.X <= 0)
{
//小球运动到左下角
ballPosition.X = 0;
VelocityX *= -restitution;
}

if (ballPosition.Y <= 0)
{
//小球运动到上边界的时候,则设置其Top值不能小于0,改变其Y轴运动方向
ballPosition.Y = 0;
VelocityY *= -restitution;
}
else if ((ballPosition.Y + this.Height) >= RootHeight)
{
//小球运动到下边界的时候,则设置其Top值为容器高度减自身高度,改变其Y轴运动方向,
//每次运动到底部都要根据虚拟的摩擦力来使X轴的速度减慢
ballPosition.Y = RootHeight - this.Height;
VelocityY *= -restitution;
VelocityX *= friction;
}
else if ((ballPosition.X + this.Width) >= RootWidth)
{
//小球运动到右边界,则设置其Left值最大值为容器宽度减自身宽度,并改变X轴速度的方向
ballPosition.X = RootWidth - this.Width;
VelocityX *= -restitution;
}
else if (ballPosition.X <= 0)
{
//小球运动到左边界,则设置其Left值最小为0,不能超出边界,并改变X轴速度的方向
ballPosition.X = 0;
VelocityX *= -restitution;
}


//设置小球最新位置
Canvas.SetLeft(this, ballPosition.X);
Canvas.SetTop(this, ballPosition.Y);
Move.Begin();
}

void ball1_MouseMove(object sender, MouseEventArgs e)
{
FrameworkElement item = sender as FrameworkElement;
if (isMouseCaptured)
{
//记录鼠标点击点位置
oldMouse.X = mousePosition.X;
oldMouse.Y = mousePosition.Y;

//计算当前对象的坐标位置,根据扑捉点位置和当前位置计算
double deltaV = e.GetPosition(null).Y - mousePosition.Y;
double deltaH = e.GetPosition(null).X - mousePosition.X;
//计算小球最新位置
double newTop = deltaV + Canvas.GetTop(item);
double newLeft = deltaH + Canvas.GetLeft(item);
//设置对象的新位置
Canvas.SetTop(this, newTop);
Canvas.SetLeft(this, newLeft);

//检测小球拖动过程中是否触碰到边界
if (Canvas.GetLeft(this) < 0 || Canvas.GetTop(this) < 0)
{
outOfBounds(item);
}
if (Canvas.GetLeft(this) + this.Width >= RootWidth)
{
outOfBounds(item);
}
if (Canvas.GetTop(this) + this.Height >= RootHeight)
{
outOfBounds(item);
}

mousePosition = e.GetPosition(null);
//根据拖拽前后位置的差值设置小球运动的启始速度
VelocityX = (mousePosition.X - oldMouse.X) / 2;
VelocityY = (mousePosition.Y - oldMouse.Y) / 2;
}
}

void ball1_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
FrameworkElement item = sender as FrameworkElement;
isMouseCaptured = false;
item.ReleaseMouseCapture();
mousePosition.X = mousePosition.Y = 0;
item.Cursor = null;

//小球初始位置
ballPosition.X = Canvas.GetLeft(this);
ballPosition.Y = Canvas.GetTop(this);

Move.Begin();
}

void ball1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
//捕捉焦点、停止动画、从新计算鼠标位置
Move.Stop();
FrameworkElement item = sender as FrameworkElement;
mousePosition = e.GetPosition(null);//获取鼠标位置
isMouseCaptured = true;
item.CaptureMouse();
item.Cursor = Cursors.Hand;

VelocityX = 0;
VelocityY = 0;
}

/// <summary>
/// 处理小球超出边界的行为,当执行此方法将释放焦点,并开始动画
/// </summary>
private void outOfBounds(FrameworkElement item)
{
isMouseCaptured = false;
item.ReleaseMouseCapture();
item.Cursor = null;
ballPosition.X = Canvas.GetLeft(this);
ballPosition.Y = Canvas.GetTop(this);
Move.Begin();
}
}

 

二、gravityBall对象

此对象容器,承载小球

gravityBall.xaml代码

<Canvas x:Name="LayoutRoot" Background="White" Width="800" Height="600">
<Rectangle x:Name="borderStroke" Width="800" Height="600" Stroke="#FF000000" Canvas.Left="0" Canvas.Top="0">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF03174D" Offset="1"/>
<GradientStop Color="#FFC8C8C8" Offset="0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Slider Width="150" Canvas.Top="38" Canvas.Left="12" x:Name="gravitySlider"/>
<TextBlock Text="重 力: " TextWrapping="Wrap" Canvas.Left="14" Canvas.Top="10" x:Name="msgGravity"/>
</Canvas>

 

gravityBall.xaml.cs代码

public partial class gravityBall : Page
{
private ball redBall;
//初始重力
private double globalGravity = .6;

public gravityBall()
{
InitializeComponent();

redBall = new ball();
gravitySlider.Minimum = 0;
gravitySlider.Maximum = 2;
gravitySlider.Value = globalGravity;
msgGravity.Text = "重 力: " + globalGravity;

Canvas.SetLeft(redBall, 100);
Canvas.SetTop(redBall, 100);
redBall.Gravity = globalGravity;
redBall.RootWidth = LayoutRoot.Width;
redBall.RootHeight = LayoutRoot.Height;
LayoutRoot.Children.Add(redBall);

gravitySlider.ValueChanged += new RoutedPropertyChangedEventHandler<double>(gravitySlider_ValueChanged);
}

private void gravitySlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
//更改小球的重力
globalGravity = gravitySlider.Value;
redBall.Gravity = globalGravity;
msgGravity.Text = "重 力: " + globalGravity.ToString("0.00");
}
}

 

至此整个示例的的代码完成。

运行效果演示地址:点击查看

 

总结:使用向量模拟了物体重力效果,主要在于对向量的使用,以及在根据给定的重力情况下去处理小球的运动轨迹。
 

 

 【注:本文技术论点源于《Foundation Silverlight 3 Animation》,个人理解可能存在差异,请参考原著】

 

 
posted @ 2010-12-17 13:17  vvince  阅读(380)  评论(0编辑  收藏  举报