Wiki


Wiki Table of Contents

Page Details

Published by:
This page has not yet been rated

Silverlight Tip of the Day #30: Fast Sprite Animation in Silverlight

Filed under: [Edit Tags]

Trong bài hướng dẫn này, tôi sẽ chứng minh cho bạn thấy làm cách nào để tạo ra một lớp Sprite chuyển động nhanh, tối ưu hóa hình ảnh . Trong demo của tôi, bạn sẽ có thể:

  1. Tăng tốc độ của những sprites (yêu tinh).
  2. Tăng số lượng của những  sprites( yêu tinh).

Để tạo điểm nhấn cho các yêu tinh thêm nữa , tôi đã thêm vào sự chuyển động ngẫu  nhiên  (nhảy lên những bức tường), xoay chuyển vị trí và tăng độ đậm nhạt cho các yêu tinh.

Chạy ứng dụng ngay bây giờ bằng cách click vào link sau: http://silverlight.services.live.com/invoke/66033/Fast%20Sprites/iframe.html.  Nếu bạn tăng tốc độ và số lượng yêu tinh lên mức độ lớn thì bạn nên chú ý là chuyển động vẫn tiếp tục mượt mà về hình ảnh và tốc độ.

Màn hình xem trước  (bên trái  = 100 yêu tinh, bên phải = 1000 yêu tinh)

image  image

Mỗi yêu tinh được kết trong lớp của nó mà tôi gọi là  Sprite. Hãy nhìn vào  class hoàn thiện cho một  Sprite bên dưới. Tóm lại:

  1. Lớp  Sprite kế thừa từ lớp  Control. Bằng cách này bạn có thể thêm trực tiếp  Sprite của bạn vào cây Canvas . Mã  XAML còn lại là đại diện cho một sprite được đặt trong một  template (_spriteTemplate) và được áp dụng cho một lớp gọi là  ApplyTemplate();
  2. Hàm khởi tạo Sprite khởi tạo chiều rộng và chiều dài của  sprite. Nó còn tải và áp dụng các template.
  3. Để gán hình ảnh của Sprite bạn phải gọi SetImage() bằng tên file của hình ảnh nguồn mà bạn đã copy hình ảnh đó vào dự án của bạn.
  4. Phần code còn lại của lớp này là chuyển động, vị trí và xoay chuyển của  Sprite.
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Markup;
 
namespace FastAnimation
{
    public class Sprite : Control
    {
        private Image _spriteImage;
        private RotateTransform _rotateTransform;
        private int _width;
        private int _height;
        private double _posX = 0;
        private double _posY = 0;
        private double _xInc = 0;
        private double _yInc = 0;
        private int _opacityDir = 1;
 
        private string _spriteTemplate =
          "<ControlTemplate xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"" +
          "                  xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">" +
          "<Image x:Name=\"SpriteImage\">" +
          "   <Image.RenderTransform>" +
          "      <RotateTransform x:Name=\"ImageTransform\">" +
          "      </RotateTransform>" +
          "   </Image.RenderTransform>" +
          "</Image>" +
          "</ControlTemplate>";
 
        public Sprite(int width, int height)
        {
            _width = width;
            _height = height;
 
            Template = (ControlTemplate)XamlReader.Load(_spriteTemplate);
            ApplyTemplate();
        }
 
        public override void OnApplyTemplate()
        {
            _spriteImage = (Image)GetTemplateChild("SpriteImage");
            _rotateTransform = (RotateTransform)GetTemplateChild("ImageTransform");
 
            _rotateTransform.CenterX = _width / 2;
            _rotateTransform.CenterY = _height / 2;
        }
 
        public void SetImage(string resource)
        {
            Uri uri = new Uri(resource, UriKind.Relative);
            ImageSource imgSrc = new System.Windows.Media.Imaging.BitmapImage(uri);
            _spriteImage.Source = imgSrc;
        }
 
 
        public double YInc
        {
            set { _yInc = value; }
        }
        public double XInc
        {
            set { _xInc = value; }
        }
 
        public double PosX
        {
            get { return _posX; }
            set
            {
                _posX = value;
                this.SetValue(Canvas.LeftProperty, _posX);
            }
        }
 
        public double PosY
        {
            get { return _posY; }
            set
            {
                _posY = value;
                this.SetValue(Canvas.TopProperty, _posY);
            }
        }
     
        public bool Step(int speed)
        {
            if (_xInc * speed + _posX > 800 || _xInc * speed + _posX < 0)
                return false;
            if (_yInc * speed + PosY > 600 || _yInc * speed + PosY < 0)
                return false;
 
            PosX += _xInc * speed;
            PosY += _yInc * speed;
 
            if (_opacityDir == 1)
                _spriteImage.Opacity += 0.01;
            else
                _spriteImage.Opacity -= 0.01;
            if (_spriteImage.Opacity >= 1)
                _opacityDir = 0;
            else if (_spriteImage.Opacity <= 0)
                _opacityDir = 1;
 
            return true;
        }
 
        public void Rotate()
        {
            _rotateTransform.Angle += 1;
            _rotateTransform.Transform(new Point(32, 24));
        }
 
    }
}

Trong trang  Page.xaml, chúng ta khai báo các thành phần  UI mà cho phép chúng ta tăng tốc độ và số lượng các sprites:

<UserControl x:Class="FastAnimation.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="800" Height="600" Background="Black">
    <Grid x:Name="LayoutRoot" Background="Black">
        <Canvas Background="Black" x:Name="MyCanvas">
            <TextBlock Canvas.ZIndex="10000" Foreground="White">Speed</TextBlock>
            <Slider Canvas.ZIndex="10000" x:Name="SliderSpeed" Minimum="0" Maximum="50" Width="100" Canvas.Left="50"></Slider>
            <TextBlock Canvas.ZIndex="10000" x:Name="SpeedValue" Canvas.Left="150"  Foreground="White">1</TextBlock>
            <TextBlock  Canvas.ZIndex="10000" Canvas.Top="20" Foreground="White">Count</TextBlock>
            <Slider Canvas.ZIndex="10000" x:Name="SliderCount"  Canvas.Top="20" Minimum="0" Maximum="1000" Value="100" Width="100" Canvas.Left="50"></Slider>
            <TextBlock Canvas.ZIndex="10000" x:Name="CountValue" Canvas.Left="150" Canvas.Top="20"  Foreground="White">100</TextBlock>
        </Canvas>
    </Grid>
</UserControl>

Trong trang  Page.xaml.cs chúng ta :

  1. Tạo một  mảng (array) của  sprites (mặc định là 100 ).
  2. Setup game  loop timer của chúng ta sử dụng Storyboard đã thảo luận trong  Tip of the Day #16. Trong vòng lặp này, chúng ta di chuyển và xoay các  sprites.
  3. KIểm soát sự kiện về Tốc độ và Số lượng Sprite bằng cách điều khiển thanh trượt slider .
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
 
namespace FastAnimation
{
    public partial class Page : UserControl
    {
        private Sprite[] _sprites;
        private Storyboard _gameLoop = new Storyboard();
        private int _spriteCount = 0;
        private int _speed = 1;
 
        public Page()
        {
            InitializeComponent();
 
            this.Loaded += new RoutedEventHandler(Page_Loaded);
         
        }
 
        void Page_Loaded(object sender, RoutedEventArgs e)
        {
            SliderCount.ValueChanged +=new RoutedPropertyChangedEventHandler<double>(SliderCount_ValueChanged);
            SliderSpeed.ValueChanged +=new RoutedPropertyChangedEventHandler<double>(SliderSpeed_ValueChanged);
            CreateSprites(100);
 
            _gameLoop.Duration = TimeSpan.FromMilliseconds(0);
            _gameLoop.Completed += new EventHandler(MainGameLoop);
            _gameLoop.Begin();
        }
 
        private void CleanUp()
        {
            for (int i = 0; i < _spriteCount; i++)
            {
                MyCanvas.Children.Remove(_spritesIdea);
            }
        }
 
        private void CreateSprites(int spriteCount)
        {
            lock (this)
            {
                CleanUp();
                _spriteCount = spriteCount;
                _sprites = new Sprite[_spriteCount];
 
                Random rand = new Random();
                for (int i = 0; i < _spriteCount; i++)
                {
                    Sprite sprite = new Sprite(64, 48);
                    double x = (double)rand.Next(800);
                    double y = (double)rand.Next(600);
                    sprite.SetImage("fireballlogo.png");
                    sprite.PosX = x;
                    sprite.PosY = y;
                    ChangeDirections(sprite);
                    MyCanvas.Children.Add(sprite);
                    _spritesIdea = sprite;
                }
            }
        }
 
        private void ChangeDirections(Sprite sprite)
        {
            Random rand = new Random();
 
            double xi = rand.Next(1, 100);
            double yi = rand.Next(1, 100);
            xi *= 0.01;
            yi *= 0.01;
            int n1 = rand.Next(0, 2);
            int n2 = rand.Next(0, 2);
            if (n1 == 0)
                xi *= -1;
            if (n2 == 0)
                yi *= -1;
            sprite.XInc = xi;
            sprite.YInc = yi;
        }
 
        private void MoveSprites()
        {
            for (int i = 0; i < _spriteCount; i++)
            {
                Sprite sprite = _spritesIdea;
                double x = sprite.PosX;
                double y = sprite.PosY;
                if (false == sprite.Step(_speed))
                {
                    ChangeDirections(sprite);
                }
            }
        }
 
        private void RotateSprites()
        {
            for (int i = 0; i < _spriteCount; i++)
                _spritesIdea.Rotate();
        }
 
        private void MainGameLoop(object sender, EventArgs e)
        {
            MoveSprites();
            RotateSprites();
            _gameLoop.Begin();
        }
 
        private void SliderCount_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            int count = (int) SliderCount.Value;
            CountValue.Text = count.ToString();
            CreateSprites(count);
        }
 
        private void SliderSpeed_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            _speed = (int) SliderSpeed.Value;
            SpeedValue.Text = _speed.ToString();
        }
 
    }
}

Thank you,
--Mike Snow

Recent Comments

Leave the first comment for this page.