zoukankan      html  css  js  c++  java
  • Silverlight C# 游戏开发:Flyer03大图裁剪,高效动画的艺术

    Flyer02最后,我们得到了一个屏幕,上面有云彩向上飘去,但是很容易发现有一个很明显的瑕疵,就是云彩不会限定在一个画面中,那么能不能控制在一个指定的范围内呢。

    Sivlerlight中限定一个UIElement的边界,可以使用Clip来实现,在能够显示图片主窗口元素上添加如下代码:

    GameMainWindow.Clip = new RectangleGeometry() { Rect = new Rect(0, 0, 400, 400) };

    现在再运行一下,看到了吧,是否达到了我们想要的效果了呢?

    是不是很有趣,让我们想想,如果能够裁剪的方式获得一个限定区域,那么是不是可以将一组图片的各个部分拆分出来呢?就如资源目录中的filyer.png,能否将每个部分拆分出来显示?Clip完全可以做到,但是并不高效,其原因是,Clip的裁剪并不会减少整张图片的大小,这意味着有多少动作帧内存中就要载入多少张整张图片,很显然,绝不优美,相信玩家也不会为此买内存的单,那么是不是就没有办法了呢?能否有一个好的方法,只加载一个张图片然后处理分成多张小图,对内存的占用就低的多了。

    可以通过WriteableBitmap来实现分成小图,WriteableBitmap是Silverlight提供操作Bitmap数据的BitmapSouce,通过这个类可以生成多张指定大小的图片,在内存上不需要加载过多的图,只需要一张大图即可,那么我们的编写思路图如下:

    通过截取整张图片当中的一个部分,渲染到WriteableBitmap当中,然后转换成为Image控件的Souce,最后通过一个定时器完成动画效果

    一如既往,我们只使用代码实现这个部分,那么需要创造一个类,这个类的名字叫ClassFlyer

    代码如下:

    Image _image; ImageSource[,] FlyerFrames = new ImageSource[6, 8]; public ClassFlyer() {
    _image = new Image();
    BitmapImage bitmap = new BitmapImage(new Uri(@"Src/flyer.png", UriKind.Relative)); bitmap.ImageOpened += new EventHandler<RoutedEventArgs>(bitmap_ImageOpened); this.Children.Add(_image); }
    //之所以在这里处理是因为Silverlight的图片下载是异步的,只有载入完毕后才好处理
    void bitmap_ImageOpened(object sender, RoutedEventArgs e)
    {
    _image.Source = sender as BitmapImage;
    for (int j = 0; j < 6; j++)
    {
    for (int i = 0; i < 8; i++) {
    WriteableBitmap wb
    = new WriteableBitmap(64,64);
    wb.Render(_image,
    new TranslateTransform()
    { X = -64 * i, Y = -64 * j });
    wb.Invalidate();
    FlyerFrames[j, i] = (ImageSource)wb; } }
    }

    最终的目的是得到了一个FlyerFrames的祯组,这组图片涵盖了以行为划分的动画序列,清晰的描述角色们的状态

    现在看起来容易多了,实现动画只是找对正确的行和列,建议实现一个简单的枚举,当然了,C#不像C++那般方便的将enum转换成为数字,所以我的实现方法是用const

     

    public class EmFlyerState { public const int 正常 = 0;
    public const int 向上 = 1;
    public const int 向右 = 2;
    public const int 向左 = 3;
    public const int 向下 = 4;
    public const int 击中 = 5;
    }

    现在下面重要的是动画,动画是按照一个时间的循环,这种有很多方法来实现,在这里为了方便理解,用一个DispatcherTimer

    DispatcherTimer dispatcherTimer = new DispatcherTimer();
    dispatcherTimer.Tick
    += new EventHandler(TickGameFrameLoop);
    dispatcherTimer.Interval
    = TimeSpan.FromMilliseconds(30); //重复间隔
    dispatcherTimer.Start();
    private void TickGameFrameLoop(object sender, EventArgs e)
    {

    }

    也许你觉得现在有点意思了,但是还无法实现控制飞行员动作,为了达到这个目的需要为MainPage加上按键事件,

    在MainPage类中加上下述代码,然后实现KeyDown和KeyUp的方法,这里做一个特别的说明,为什么还要检测KeyUp,这是因为当玩家松开键盘的时候,可以让飞行员的角色变为正常状态。

    this.KeyDown += new KeyEventHandler(MainPage_KeyDown);
    this.KeyUp += new KeyEventHandler(MainPage_KeyUp);

    在KeyDown和KeyUp事件里,我们可以写下如下代码来控制角色的状态,假设我们之前定为ClassFlyer的实例名为Hero

    void MainPage_KeyDown(object sender, KeyEventArgs e)
    {
    switch (e.Key)

    {                  

    case Key.Up:

    Hero.Flyerstate
    = EmFlyerState.向上;

    break;

    case Key.Down:

    Hero.Flyerstate
    = EmFlyerState.向下;

    break;

    case Key.Left:

    Hero.Flyerstate
    = EmFlyerState.向左;

    break;

    case Key.Right:

    Hero.Flyerstate
    = EmFlyerState.向右;

    break;

    }

    }

    void MainPage_KeyUp(object sender, KeyEventArgs e)

    {

    Hero.Flyerstate
    = EmFlyerState.正常;

    }

    基本上我们已经接近于完成,但是还有一些细节代码需要整理,在这里就不一一做讲解了,有兴趣的请直接看代码:)

    点击这里进行下载:FlyerGame2

    Get Microsoft Silverlight
  • 相关阅读:
    自定义类型转换器之TypeConverter
    python测试工具nosetests
    算法练习之相同的树,对称二叉树
    算法练习之x的平方根,爬楼梯,删除排序链表中的重复元素, 合并两个有序数组
    算法练习之报数, 最大子序和,最后一个单词的长度,加一,二进制求和
    java.sql.SQLException: Zero date value prohibited
    java打包小记
    修改jar的.class文件,并重新打包
    算法练习之合并两个有序链表, 删除排序数组中的重复项,移除元素,实现strStr(),搜索插入位置,无重复字符的最长子串
    解决GitHub访问速度慢的问题
  • 原文地址:https://www.cnblogs.com/nowpaper/p/1659303.html
Copyright © 2011-2022 走看看