Silverlight MMORPG网页游戏开发课程[一期] 第一课:控制对象移动

引言

  游戏中的主角是精灵,我们可以把游戏中的一切对象均视作精灵;玩家大部分时间都在操控着游戏中对象进行移动。因而本节我们要实现的功能只有一个:通过鼠标控制对象移动。

  1.1通过Storyboard创建对象移动动画(交叉参考: 让物体动起来①)

  在序言中我们讲解了如何通过VisualStudio2010创建一个Silverlight4项目,那么我们首先打开这个项目,并将MainPage.xaml中名为LayoutRoot的Grid换成Canvas。

  Canvas顾名思义是画布的意思,它是游戏开发中性能最高且最易于用做对象布局及移动的容器控件。

  接下来打开MainPage.xaml后台代码MainPage.xaml.cs,创建一个名为rectangle的矩形对象:

双击代码全选
1
2
3
4
5
6
7
8
/// <summary>
/// 填充色为绿色,宽、高各50的矩形
/// </summary>
Rectangle rectangle =  new  Rectangle() {
    Fill =  new  SolidColorBrush(Colors.Green),
    Width = 50,
    Height = 50
};

  然后在MainPage初始化后将该矩形对象(rectangle)作为子控件添加到LayoutRoot画布中,并为LayoutRoot注册(订阅)鼠标左键点击事件:

双击代码全选
1
2
3
4
5
6
7
public  MainPage() {
    InitializeComponent();
     //将矩形添加进画布容器中
    LayoutRoot.Children.Add(rectangle);
     //注册画布容器的鼠标左键点击事件
    LayoutRoot.MouseLeftButtonDown +=  new  MouseButtonEventHandler(LayoutRoot_MouseLeftButtonDown);
}

  最后,要实现通过鼠标左键点击来控制对象移动,我们还得在LayoutRoot_MouseLeftButtonDown方法体中编写相应的移动动画实现逻辑(在整个画布任意位置点击左键时,矩形对象rectangle即向该点移动):

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private   void  LayoutRoot_MouseLeftButtonDown( object  sender, MouseButtonEventArgs e) {
     //获取点击处相对于容器的坐标位置
    Point p = e.GetPosition(LayoutRoot);
     //创建移动用的动画故事板
    Storyboard storyboard =  new  Storyboard();
     //创建X轴方向动画
    DoubleAnimation xAnimation =  new  DoubleAnimation() {
         //起点
        From = Canvas.GetLeft(rectangle),
         //终点
        To = p.X,
         //花费时间
        Duration =  new  Duration(TimeSpan.FromMilliseconds(500)),
    };
     //将X轴方向动画赋予rectangle
    Storyboard.SetTarget(xAnimation, rectangle);
     //设置X轴方向动画所影响的对象属性为Canvas.Left
    Storyboard.SetTargetProperty(xAnimation,  new  PropertyPath( "(Canvas.Left)" ));
     //将X轴方向动画添加进动画故事板中
    storyboard.Children.Add(xAnimation);
     //创建Y轴方向动画
    DoubleAnimation yAnimation =  new  DoubleAnimation() {
        From = Canvas.GetTop(rectangle),
        To = p.Y,
        Duration =  new  Duration(TimeSpan.FromMilliseconds(500)),
    };
    Storyboard.SetTarget(yAnimation, rectangle);
     //设置Y轴方向动画所影响的对象属性为Canvas.Top
    Storyboard.SetTargetProperty(yAnimation,  new  PropertyPath( "(Canvas.Top)" ));
    storyboard.Children.Add(yAnimation);
     //播放动画
    storyboard.Begin();
}

以X轴方向动画实现为例(以上代码黄色部分),首先创建一个有起点、终点以及总耗时的DoubleAnimation:xAnimation,然后通过Storyboard.SetTarget(xAnimation,rectangle)这句将xAnimation动画过程绑定到矩形对象rectangle,并且通过Storyboard.SetTargetProperty(xAnimation,new PropertyPath(“(Canvas.Left)”))这行代码告诉xAnimation动画它影响的是对象(rectangle)的Canvas.Left属性。这两句话的写法是固定的,有兴趣的朋友可以将(Canvas.Left)换成Opacity试试(注意Opacity不需要括号),从运行结果去理解体会这两行代码的重要意义。

  同样的,Y轴方向的动画依葫芦画瓢,唯一不同的是将Canvas.Left改成了Canvas.Top。

  完成后在VisualStudio2010中按下Ctrl+F5进行编译运行(仅F5可进入调试运行):

  通过本节的学习,一方面大家要学会使用Storyboard创建对象移动动画;另一方面也要掌握在Canvas画布容器中改变对象位置即X、Y坐标的方法分别是Canvas.SetLeft(对象,x坐标)和Canvas.SetTop(对象,y坐标)。

  在Silverlight中除了可以使用Storyboard来创建移动动画外,还有另外两个选择:CompositionTarget和DispatcherTimer。

  1.2通过CompositionTarget创建对象移动动画(交叉参考: 让物体动起来②)

  CompositionTarget对象可以根据每个帧的回调来创建自定义动画。通俗的讲就是CompositionTarget创建的动画是基于画面每次刷新时触发的,与窗体刷新率保持一致,因而频率是相对固定的(随Silverlight应用程序的FPS而变化),人工无法介入控制。

  那么如何使用它来实现1.1中一模一样的移动动画效果呢?

  与1.1的步骤一样,我们首先创建一个矩形对象(当圆角半径(RadiusX、RadiusY)与相应的边长(Width、Height)相等时呈圆形),并在MainPage初始化后将之添加进画布同时注册画布鼠标左键点击事件:

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/// <summary>
/// 填充色为蓝色,宽、高、圆角X、Y各50的矩形
/// </summary>
Rectangle rectangle =  new  Rectangle() {
    Fill =  new  SolidColorBrush(Colors.Blue),
    Width = 50,
    Height = 50,
    RadiusX = 50,
    RadiusY = 50
};
 
public  MainPage() {
    InitializeComponent();
     //将矩形添加进画布容器中
    LayoutRoot.Children.Add(rectangle);
     //注册画布容器的鼠标左键点击事件
    LayoutRoot.MouseLeftButtonDown +=  new  MouseButtonEventHandler(LayoutRoot_MouseLeftButtonDown);
}

  因为是基于画面不停的刷新实现的对象位置改变,因此此时的鼠标左键事件中我们需要做的是记录鼠标移动的目的以及每次移动X、Y方向的速度增量(根据三角函数中的等边比例算出)和移动次数,同时依据具体情况注册/注销CompositionTarget.Rendering事件:

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
bool  isRendering =  false ;  //是否启动了刷新
double  speed = 10;  //每次移动10像素
double  xSpeed, ySpeed;  //X、Y方向的速度
int  num;  //需要移动次数
int  count;  //统计移动次数
private   void  LayoutRoot_MouseLeftButtonDown( object  sender, MouseButtonEventArgs e) {
     if  (!isRendering) {
        CompositionTarget.Rendering +=  new  EventHandler(Rendering);
        isRendering =  true ;
    }
    Point start =  new  Point(Canvas.GetLeft(rectangle), Canvas.GetTop(rectangle));
    Point end = e.GetPosition(LayoutRoot);
     double  distance = Math.Sqrt(Math.Pow((end.X - start.X), 2) + Math.Pow((end.Y - start.Y), 2));
    xSpeed = (end.X - start.X) * speed / distance;
    ySpeed = (end.Y - start.Y) * speed / distance;
    num = ( int )(distance / speed);
    count = 0;
}

最后编写CompositionTarget_Rendering方法体逻辑实现矩形移动:

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
private   void  Rendering( object  sender, EventArgs e) {
     double  x = Canvas.GetLeft(rectangle);
     double  y = Canvas.GetTop(rectangle);
    Canvas.SetLeft(rectangle, x + xSpeed);
    Canvas.SetTop(rectangle, y + ySpeed);
     if  (count == num) {
        CompositionTarget.Rendering -= Rendering;
        isRendering =  false ;
    }
    count++;
}

  大体思路是:每次移动后记数count++,当移动到总次数num时将CompositionTarget.Rendering事件注销(取消订阅)掉,从而实现到达目的地时对象自动停止的效果。

  1.3通过DispatcherTimer创建对象移动动画(交叉参考: 让物体动起来③  实现2D人物动画①  实现2D人物动画②)

  DispatcherTimer顾名思义是一个计时器,它的Tick事件类似于在Silverlight界面线程中进行Join操作;并且基于Silverlight/WPF的Dispatcher机制,我们无需考虑任何跨线程问题。因此DispatcherTimer在Silverlight游戏开发中应用非常广泛。

  同样具备“心跳”特性的DispatcherTimer实现对象移动逻辑非常类似1.2中的CompositionTarget,唯一不同的是DispatcherTimer是完全可控的。于是我们仅仅需要做的是在1.2代码基础上稍动些手脚即可,具体如下:

  1)  定义一个间隔为10毫秒的DispatcherTimer对象:

双击代码全选
1
2
3
4
//计时器对象
DispatcherTimer dispatcherTimer =  new  DispatcherTimer() {
     Interval = TimeSpan.FromMilliseconds(10)
};

  2)在MainPage()中注册Tick事件:

  dispatcherTimer.Tick += new EventHandler(Rendering);

  3)将CompositionTarget.Rendering += new EventHandler(Rendering);修改为dispatcherTimer.Tick += new EventHandler(Rendering);

  4)同样的将CompositionTarget.Rendering -= Rendering;修改为dispatcherTimer.Stop();

  很简单对吧。由此可见DispatcherTimer实现动画的原理与CompositionTarget确实非常类似,只是我们可以通过Start()和Stop()更方便的启动/停止DispatcherTimer对象。

  以上三种方式均能实现鼠控制对象移动,那么我们该如何将之与游戏联系起来呢?

  大家是否已迫不及待的想要把真实的精灵加入到游戏画布中?此时的Rectangle将再次大显身手,我们可以通过为其设置宽、高并填充上PNG图象,转眼间一个华丽的精灵即呈现在我们眼前:

双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
/// <summary>
/// 填充为一张宽高均150像素的PNG精灵图片矩形
/// </summary>
Rectangle rectangle =  new  Rectangle() {
    Fill =  new  ImageBrush() {
        ImageSource =  new  BitmapImage() {
            UriSource =  new  Uri( @"/Lesson1.3;component/Res/Sprite.png" , UriKind.Relative)
        },
    },
    Width = 150,
    Height = 150
};

  Silverlight用于图象呈现的载体除Rectangle外还有Image;Image使用起来更智能,它可以根据图像源的宽高自适应尺寸,我们需要做的仅仅是为其设置源文件路径而已:

双击代码全选
1
2
3
4
5
6
7
8
/// <summary>
/// 图像对象
/// </summary>
Image image =  new  Image() {
    Source =  new  BitmapImage() {
        UriSource =  new  Uri( @"/Lesson1.3;component/Res/Sprite.png" , UriKind.Relative)
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值