Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Frame-based Bitmap Animations in Silverlight

Despite the fact that I have woefully neglected my incipient series on developing a WPF application with TDD (aka ChumChase), like many others this week I've been taken by the Silverlight Lust.

I decided to make a simple game, and settled on an Asteroids clone. I drew some inspiration and insight from the blogs of Bill Reiss, Andy Beaulieu, and Robby Ingebretsen.

I started off with a basic triangular ship, drawn using this:

<UserControl x:Class="BlueSpire.Roids.Symbols.StarShip"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Canvas x:Name="LayoutRoot" 
            Width="50" Height="105">
        <Path x:Name="Ship"
              Fill="#FF1C8249" 
              Stretch="Fill" 
              Stroke="#FFFFFFFF" 
              Data="M50,0 L25,105 L50,80 L75,105 z"/>
    </Canvas>
</UserControl>

a very simple vector ship I was more interested in gameplay, than graphics. :-)

However, as my imagination began to flame, I had a idea that required animated bitmap graphics.

Animations that use bitmaps are essentially little flip-books, where you display a sequence of bitmaps in rapid succession providing the illusion of animation. There are lots of ways to achieve this technically. I found a StackOverflow question asking about the best approach. The aforementioned Bill Reiss provided an answer and I decided to implement it.

Sprite Sheet

First you create a single image that contains all of frames. For my proof of concept, I decided to make a little engine glow over the course of 8 frames. The results was the 512x64 image.

an example sprite sheet

The idea is that we display just a portion of the image, in this case 64x64 square portion. The actual UIElement that will represent the ship on screen is a Rectangle. We'll make it 64x64 in size, and set it's Fill property with an ImageBrush pointing to my sprite sheet. Then I animated a TranslateTransform on the ImageBrush to display the each frame in sequence. Well, code speaks louder than words, so:

private void SpriteSheetTest()
{
    const int sprite_size = 64;    //the frame is 64x64
    const int num_of_frames = 8;
    
    var ship_element = new Rectangle();
    ship_element.Width = ship_element.Height = sprite_size;

    var ship_sprite_sheet = new ImageBrush
                                {
                                    Stretch = Stretch.None,
                                    AlignmentX = AlignmentX.Left,
                                    AlignmentY = AlignmentY.Top
                                };

    // If the brush alignment is not set, the image will be
    // centered and translation transform won't be right. 

    var sprite_sheet_position = new TranslateTransform();
    ship_sprite_sheet.Transform = sprite_sheet_position;
    ship_sprite_sheet.ImageSource = ResourceHelper.GetBitmap("Resources/Roid.png");

    ship_element.Fill = ship_sprite_sheet;

    // I'm dynamically creating the frames, displaying a 
    // new frame every 80 ms.
    var sprite_anim = new DoubleAnimationUsingKeyFrames();
    for (int i = 0; i < num_of_frames; i++)
    {
        var frame_span = new TimeSpan(0, 0, 0, 0, i * 80);
        sprite_anim.KeyFrames.Add(new DiscreteDoubleKeyFrame
                             {
                                 Value = (-sprite_size*i),
                                 KeyTime = KeyTime.FromTimeSpan(frame_span)
                             });
    }

    // finally, we need a storyboard to tie the animation
    // to the transform
    var sb = new Storyboard {RepeatBehavior = RepeatBehavior.Forever};
    sb.Children.Add(sprite_anim);
    Storyboard.SetTarget(sprite_anim, sprite_sheet_position);
    Storyboard.SetTargetProperty(sprite_anim,
                     new PropertyPath(TranslateTransform.XProperty));
    sb.Begin();


    //Stage is a Canvas that represent the main area of the game
    Stage.Children.Add(ship_element);
}

The ResourceHelper class is a handy bit for working with embedded resources, that I picked up here. Hint, embedded resources have a Build Action of Resource and not Embedded Resource. I wasted 15 or 20 minutes on that one...


Posted 10-17-2008 11:39 AM by Christopher Bennage

[Advertisement]

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)