第二种方法,CompositionTarget动画,官方描述为:CompositionTarget对象可以根据每个帧回调来创建自定义动画。其实直接点,CompositionTarget创建的动画是基于每次界面刷新后触发的,与窗体刷新率保持一致,所以频率是固定的,很难人工介入控制。
那么如何使用它?xaml的界面代码还是和上一篇中描述的一样,这里不累述了。那么接下来就是创建对象并注册事件,全部代码如下:
Rectangle rect; //创建一个方块作为演示对象
double speed = 1; //设置移动速度
Point moveTo; //设置移动目标
public Window1() {
InitializeComponent();
rect = new Rectangle();
rect.Fill = new SolidColorBrush(Colors.Red);
rect.Width = 50;
rect.Height = 50;
rect.RadiusX = 5;
rect.RadiusY = 5;
Carrier.Children.Add(rect);
Canvas.SetLeft(rect, 0);
Canvas.SetTop(rect, 0);
//注册界面刷新事件
CompositionTarget.Rendering += new EventHandler(Timer_Tick);
}
private void Carrier_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
moveTo = e.GetPosition(Carrier);
}
CompositionTarget的注册事件方法为:
CompositionTarget.Rendering += new EventHandler(Timer_Tick);
因为我们要实现的是鼠标点哪方块就移动到哪,所以我用一个变量moveTo保存鼠标点击点的Point。并在鼠标左键事件中赋值:moveTo = e.GetPosition(Carrier);同时设置方块X,Y方向的速度均为speed。
接下来就是实现Timer_Tick了,它是基于窗体的时时刷新事件。我们这样写:
private void Timer_Tick(object sender, EventArgs e) {
double rect_X = Canvas.GetLeft(rect);
double rect_Y = Canvas.GetTop(rect);
Canvas.SetLeft(rect, rect_X + (rect_X < moveTo.X ? speed : -speed));
Canvas.SetTop(rect, rect_Y + (rect_Y < moveTo.Y ? speed : -speed));
}
首先获取方块的X,Y位置,接下让方块的X,Y与moveTo的X,Y进行比较而判断是+speed还是-speed,这里的逻辑需要朋友们自行领会了。
好了Ctrl+F5测试一下,呵呵,是不是同样也动起来了呢?
可是大家会发现一个很大的问题:这方块移动得也太勉强了吧,抖来抖去的而且移动得也不平滑,是不是CompositionTarget有问题?其实不然,因为之前的Storyboard动画它不存在X,Y轴的速度,只需要设定起点和终点以及过程经历的时间就可以平滑的移动了,而CompositionTarget需要分别设定X,Y轴的速度,而我们这为了简单演示,X,Y轴的速度speed均设置成了5,这在现实使用中是绝对不合理的。因此,如果要模拟实际效果,必须计算终点和起点的正切值Tan,然后再根据直线速度speed通过Tan值计算出speed_X,speed_Y,最后改写成:
Canvas.SetLeft(rect, rect_X + (rect_X < moveTo.X ? speed_X : -speed_X));
Canvas.SetTop(rect, rect_Y + (rect_Y < moveTo.Y ? speed_Y : -speed_Y));
这样才能实现真实的移动(具体算法就不讨论了)。
这一节讲解了如何使用CompositionTarget主界面刷新线程实现基于帧的动画,下一节我将讲解第三种动态创建动画的方法,并会对这三种方法进行一个归纳比较。
作者:深蓝色右手
出处:http://alamiye010.cnblogs.com/
教程目录及源码下载:点击进入(欢迎加入WPF/Silverlight小组 WPF/Silverlight博客团队)
本文版权归作者和博客园共有,欢迎转载。但未经作者同意必须保留此段声明,且在文章页面显著位置给出原文连接,否则保留追究法律责任的权利。
Tag标签: WPF/Silverlight动画游戏教程
深蓝色右手
关注 - 38
粉丝 - 250
1
0
0
(请您对文章做出评价)
« 上一篇:C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(一)让物体动起来①
» 下一篇:C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三)让物体动起来③
posted on 2009-06-17 21:04 深蓝色右手 阅读(9703) 评论(41) 编辑 收藏
Feedback
1876220
#1楼2009-06-18 09:32 | 花纯春
2009-06-18 12:46 | sparks345
第1节课就出师不利啊⊙﹏⊙b
为什么我的这里报错哇 Storyboard下就没有SetTarget方法
Storyboard.SetTarget
还有这里,说缺参数
storyboard.Begin();
回复 引用 查看
2009-06-18 13:14 | W.SiMin[未注册用户]
个人感觉XNA比较缺乏GUI系统,要自己写,然后注入进去,还有其他的比如物理啊,什么的都要自己实现,比起C++那么多的开源组件,XNA显得比较蹩脚,就像当年的DIRECTX.NET
回复 引用
#4楼[楼主]
2009-06-18 15:26 | 深蓝色右手
@sparks345
注意添加引用,需要引用Media类
回复 引用 查看
2009-06-18 21:06 | 凯锐
@sparks345
是你的.net framework沒有打sp1,這個Storyborad在sp1中有修改過的!
博主在目錄中就指出了在vs2008 sp1 +以上開發的呀!
呵呵.....
回复 引用 查看
2009-06-22 10:39 | 丝丝[未注册用户]
2009-06-22 16:40 | yuhenyunchou[未注册用户]
private void Timer_Tick(object sender, EventArgs e)
{
var rectX = Canvas.GetLeft(rectangle);
var rectY = Canvas.GetTop(rectangle);
if (moveTo.X == rectX && moveTo.Y == rectY) return;
var width = Math.Abs(rectX - moveTo.X);
var height = Math.Abs(rectY - moveTo.Y);
var speedX = width > height ? speed : Width * speed / height;
var speedY = width > height ? height * speed / width : speed;
Canvas.SetLeft(rectangle, rectX + (rectX < moveTo.X ? speedX : -speedX));
Canvas.SetTop(rectangle, rectY + (rectY < moveTo.Y ? speedY : -speedY));
}
回复 引用
2009-06-22 16:42 | 雨恨云愁
2009-06-22 17:14 | 雨恨云愁
疏忽了,上面的代码
var speedX = width > height ? speed : Width * speed / height;
应该改成:
var speedX = width > height ? speed : width * speed / height;
回复 引用 查看
#10楼[楼主]
2009-06-22 18:31 | 深蓝色右手
2009-06-27 18:10 | Fencer_HAHA[未注册用户]
@雨恨云愁
你在9楼写的代码不一样吗?
而且加上后有时候还是不稳定
回复 引用
2009-07-04 17:44 | ianc
画面抖动的原因应该不是博主讲的那样
private void Timer_Tick(object sender, EventArgs e) {
double rect_X = Canvas.GetLeft(rect);
double rect_Y = Canvas.GetTop(rect);
if (Math.Abs(rect_X - moveTo.X) > 1)
{
Canvas.SetLeft(rect, rect_X + (rect_X < moveTo.X ? speed : -speed));
Canvas.SetTop(rect, rect_Y + (rect_Y < moveTo.Y ? speed : -speed));
}
}
这样就可以了。
回复 引用 查看
2009-07-05 17:34 | mienfong[未注册用户]
樓上少了Y軸,是下面的寫法才對。
double rect_X = Canvas.GetLeft(rect);
double rect_Y = Canvas.GetTop(rect);
if (Math.Abs(rect_X - moveTo.X) > 1 || Math.Abs(rect_Y - moveTo.Y) > 1)
{
Canvas.SetLeft(rect, rect_X + (rect_X < moveTo.X ? speed : -speed));
Canvas.SetTop(rect, rect_Y + (rect_Y < moveTo.Y ? speed : -speed));
}
回复 引用
#14楼[楼主]
2009-07-05 18:03 | 深蓝色右手
@mienfong
@ianc
呵呵,感谢大家的支持!
回复 引用 查看
2009-07-10 16:16 | 乡村里的.NET
2009-07-18 09:50 | 刑ˇ天
#17楼[楼主]
2009-07-18 17:16 | 深蓝色右手
2009-07-23 17:03 | sdhj
Rectangle rect;
double speed = 1;
double speed_X;
double speed_Y;
Point moveTo=new Point(0,0);
public Window2()
{
InitializeComponent();
rect = new Rectangle();
rect.Fill = new SolidColorBrush(Colors.Red);
rect.Width = 50;
rect.Height = 50;
rect.RadiusX = 5;
rect.RadiusY = 5;
Carrier.Children.Add(rect);
Canvas.SetLeft(rect, 0);
Canvas.SetTop(rect, 0);
//注册界面刷新事件
CompositionTarget.Rendering += new EventHandler(Timer_Tick);
}
private void Carrier_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
moveTo = e.GetPosition(Carrier);
double rect_X = Canvas.GetLeft(rect);
double rect_Y = Canvas.GetTop(rect);
double x = Math.Abs(moveTo.X - rect_X);
double y = Math.Abs(moveTo.Y - rect_Y);
double len = Math.Sqrt(x * x + y * y);
if (len != 0)
{
speed_X = (x / len) * speed;
speed_Y = (y / len) * speed;
}
else
{
speed_X = speed;
speed_Y = speed;
}
}
private void Timer_Tick(object sender, EventArgs e)
{
double rect_X = Canvas.GetLeft(rect);
double rect_Y = Canvas.GetTop(rect);
Canvas.SetLeft(rect, rect_X + (rect_X < moveTo.X ? speed_X : -speed_X));
Canvas.SetTop(rect, rect_Y + (rect_Y < moveTo.Y ? speed_Y : -speed_Y));
}
回复 引用 查看
2009-07-30 21:18 | 为理想在路上奔跑
楼主,我把那个speed改大了点,抖的好厉害哦,这是什么原因,是应为刷新的问题么
回复 引用 查看
2009-08-05 17:15 | wangxj[未注册用户]
DoubleAnimation doubleAnimation = new DoubleAnimation( Canvas.GetLeft(rect), p.X,new Duration(TimeSpan.FromMilliseconds(500)));
我在wpf里,这句可以通过,在silverlight里,我句报错,wpf和sl不一样吗?
回复 引用
#21楼[楼主]
2009-08-05 18:22 | 深蓝色右手
@wangxj
写法上会有区别,功能上也是有差距的,毕竟SL是子集
回复 引用 查看
2009-08-08 16:21 | 快乐的流浪汉
刚开始学你的这个系列教程,很好!!!
好象还没有人解决speed改大抖的厉害的问题哦!
我改了一下,解决了这个问题,供参考一下,呵呵!!!!!
其他部分代码一样,主要改动如下:
private void Timer_Tick(object sender, EventArgs e)
{
double rect_X = Canvas.GetLeft(rect);
double rect_Y = Canvas.GetTop(rect);
if (moveTo.X == rect_X && moveTo.Y == rect_Y)
return;
double width = Math.Abs(rect_X - moveTo.X);
double height = Math.Abs(rect_Y - moveTo.Y);
double judgeSpeed = width > height ? width : height;
if (judgeSpeed > speed)
{
double speed_X = width > height ? speed : width * speed / height;
double speed_Y = width > height ? height * speed / width : speed;
Canvas.SetLeft(rect, rect_X + (rect_X < moveTo.X ? speed_X : -speed_X));
Canvas.SetTop(rect, rect_Y + (rect_Y < moveTo.Y ? speed_Y : -speed_Y));
}
else
{
Canvas.SetLeft(rect, moveTo.X);
Canvas.SetTop(rect, moveTo.Y);
}
}
回复 引用 查看
2009-08-09 21:23 | sparks345
double rectX = Canvas.GetLeft(rect);
double rectY = Canvas.GetTop(rect);
if (Double.IsNaN(rectY))
rectY = 0;
Canvas.SetLeft(rect, rectX +( rectX > moveTo.X ? -speed : speed));
double lx = Math.Abs(moveTo.X - rectX);
double ly = Math.Abs(moveTo.Y - rectY);
double speedY = ly * speed / lx;
Canvas.SetTop(rect, rectY + (rectY > moveTo.Y ? -speedY : speedY));
------------------
不知道为什么 Canvas.GetTop(rect) 会返回 NaN~~
好郁闷~~
回复 引用 查看
#24楼[楼主]
2009-08-09 21:43 | 深蓝色右手
不行你先设置一下Canvas.SetTop(rect)调试一下
回复 引用 查看
2009-08-18 14:39 | peerless
呵呵呵 这是很不错的教程啊。。。。
谢谢楼主
我这样做了一使矩形不抖
Rectangle rect; //创建一个方块作为演示对象
double actionTime = 400; //设置动作时间(相对于屏幕刷新次数的时间,如400即为屏幕刷新400次需要的时间)
double speedX = 0;
double speedY = 0;
Point moveTo; //设置移动目标
public MainPage()
{
InitializeComponent();
rect = new Rectangle();
rect.Fill = new SolidColorBrush(Colors.Red);
rect.Width = 50;
rect.Height = 50;
rect.RadiusX = 5;
rect.RadiusY = 5;
Carrier.Children.Add(rect);
Canvas.SetLeft(rect, 0);
Canvas.SetTop(rect, 0);
//注册界面刷新事件
CompositionTarget.Rendering += new EventHandler(Timer_Tick);
}
private void Carrier_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
moveTo = e.GetPosition(Carrier);
speedX = (moveTo.X - Canvas.GetLeft(rect))/actionTime;
speedY = (moveTo.Y - Canvas.GetTop(rect))/actionTime;
}
private void Timer_Tick(object sender, EventArgs e)
{
double rect_X = Canvas.GetLeft(rect);
double rect_Y = Canvas.GetTop(rect);
if ((moveTo.X - Canvas.GetLeft(rect) < 1) && (moveTo.X - Canvas.GetLeft(rect) > -1))
speedX = 0;
if ((moveTo.Y - Canvas.GetTop(rect) <1) && (moveTo.Y - Canvas.GetTop(rect) > -1))
speedY = 0;
Canvas.SetLeft(rect, rect_X + speedX);
Canvas.SetTop(rect, rect_Y + speedY);
}
回复 引用 查看
2009-08-20 09:49 | 卷毛[未注册用户]
产生闪烁的主要原因如下:
speed代表了方块每次移动的距离,当最后一次移动时,会超过moveTo所指定值,所以,要比较每次移动的预期位置与moveTo,当预期位置小于moveTo时,移动到预期的位置,否则移动到moveTo。
具体代码如下,请各位指正:
private void Timer_Tick(object sender, EventArgs e)
{
double rect_X = Canvas.GetLeft(rect);
double rect_Y = Canvas.GetTop(rect);
if (rect_X == moveTo.X && rect_Y == moveTo.Y)
return;
double width = Math.Abs(rect_X - moveTo.X);
double height = Math.Abs(rect_Y - moveTo.Y);
double len = Math.Sqrt(width * width + height * height);
double speed_X = speed * width / len;
double speed_Y = speed * height / len;
double offset_X = rect_X < moveTo.X ? speed_X : -speed_X;
double offset_Y = rect_Y < moveTo.Y ? speed_Y : -speed_Y;
if(Math.Abs(rect_X - moveTo.X) > offset_X)
Canvas.SetLeft(rect, rect_X + offset_X);
else
Canvas.SetLeft(rect, moveTo.X);
if (Math.Abs(rect_Y - moveTo.Y) > offset_Y)
Canvas.SetTop(rect, rect_Y + offset_Y);
else
Canvas.SetTop(rect, moveTo.Y);
}
回复 引用
2009-12-16 14:59 | 哈哈哈由于[未注册用户]
引用卷毛:
private void Timer_Tick(object sender, EventArgs e)
{
double rect_X = Canvas.GetLeft(rect);
double rect_Y = Canvas.GetTop(rect);
if (rect_X == moveTo.X && rect_Y == moveTo.Y)
return;
double width = Math.Abs(rect_X - moveTo.X);
double height = Math.Abs(rect_Y - moveTo.Y);
double len = Math.Sqrt(width * width + height * height);
double speed_X = speed * width / len;
double speed_Y = speed * height / len;
double offset_X = rect_X < moveTo.X ? speed_X : -speed_X;
double offset_Y = rect_Y < moveTo.Y ? speed_Y : -speed_Y;
if(Math.Abs(rect_X - moveTo.X) > offset_X)
Canvas.SetLeft(rect, rect_X + offset_X);
else
Canvas.SetLeft(rect, moveTo.X);
if (Math.Abs(rect_Y - moveTo.Y) > offset_Y)
Canvas.SetTop(rect, rect_Y + offset_Y);
else
Canvas.SetTop(rect, moveTo.Y);
} quote]
可以讲讲吗都是什么意思吗?
回复 引用
2010-01-12 00:36 | 采云摘月
按照楼主提出的解决方案,果然能够防止抖动!谢谢,楼主提醒啊!!
代码如下:
01
double
rect_X = Canvas.GetLeft(rect);
02
double
rect_Y = Canvas.GetTop(rect);
03
double
x = Math.Abs(moveTo.X - rect_X);
04
double
y = Math.Abs(moveTo.Y - rect_Y);
05
double
tanR;
06
if
(x == 0)
07
{
08
tanR = 0;
09
speed_Y = 0;
//
10
speed_X = 0;
11
}
12
else
13
{
14
tanR = y / x;
15
speed_Y = speed * Math.Sin(Math.Atan(tanR));
//
16
speed_X = speed * Math.Cos(Math.Atan(tanR));
17
}
18
Canvas.SetLeft(rect, rect_X + (rect_X < moveTo.X ? speed_X : -speed_X));
19
Canvas.SetTop(rect, rect_Y + (rect_Y < moveTo.Y ? speed_Y : -speed_Y));
2010-01-27 10:11 | 包建强
CompositionTarget的Rending事件上挂的方法,不用的时候最好Remove掉,不然会降低性能。
回复 引用 查看
#30楼[楼主]
2010-01-27 15:13 | 深蓝色右手
@包建强
是的,类似传统游戏中的游戏主循环。
回复 引用 查看
2010-03-23 22:39 | Coki
角度只在左键点击时才会改变,应该把计算speed_x和speed_y的过程放在鼠标点击事件里,帧更新的方法里只需要加减这个固定的速度就好了,抖动是因为某帧结束后方块从目标点一边移动到了另一边,下一帧时他又会移回来,如此往复。所以防止抖动还要加上一个判断,假如某方向当前距离小于或等于该方向的速度,直接把方块移动到目标点上即可。
01
private
void
Timer_Tick(
object
sender, EventArgs e)
02
{
03
double
rect_x = Canvas.GetLeft(rect);
04
double
rect_y = Canvas.GetTop(rect);
05
if
(Math.Abs(rect_x - moveTo.X) <= speed_x || Math.Abs(rect_y - moveTo.Y) <= speed_y)
06
{
07
Canvas.SetLeft(rect, moveTo.X);
08
Canvas.SetTop(rect, moveTo.Y);
09
}
10
else
11
{
12
Canvas.SetLeft(rect, rect_x + ((rect_x > moveTo.X) ? -speed_x : speed_x));
13
Canvas.SetTop(rect, rect_y + ((rect_y > moveTo.Y) ? -speed_y : speed_y));
14
}
15
}
16
17
private
void
Canvas_MouseLeftButtonDown(
object
sender, MouseButtonEventArgs e)
18
{
19
moveTo = e.GetPosition(carrier);
20
x = Math.Abs(Canvas.GetLeft(rect) - moveTo.X);
21
y = Math.Abs(Canvas.GetTop(rect) - moveTo.Y);
22
if
(x != 0 && y != 0)
23
{
24
double
tan = y / x;
25
speed_y = Math.Sin(Math.Atan(tan)) * speed;
26
speed_x = (1 / tan) * speed_y;
27
}
28
else
if
(x == 0 && y != 0)
29
{
30
speed_x = 0;
31
speed_y = speed;
32
}
33
else
if
(x != 0 && y == 0)
34
{
35
speed_x = speed;
36
speed_y = 0;
37
}
38
else
if
(x == 0 && y == 0)
39
{
40
speed_x = 0;
41
speed_y = 0;
42
}
43
}