Devlico.Us
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @devlicious

Christopher Bennage

Our WPF book is now available!
follow @bennage on Twitter!
 


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...



Comments

DotNetKicks.com said:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# October 17, 2008 11:47 AM

2008 October 21 - Links for today « My (almost) Daily Links said:

Pingback from  2008 October 21 - Links for today &laquo; My (almost) Daily Links

# October 21, 2008 4:34 AM

Keith said:

I have an Image element ( Img1 ) defined on the page.xaml and a button. When you click the button, I want to display image #2. So Im not really wanting a story board, just to reload the image with clipping. I have not been able to get this to work. A suggestion would be appreciated. Junk@isildo.com
# October 29, 2008 12:31 PM

Matthew Casperson said:

Check out http://silverlight.sourceforge.net/ for an example of bitmap animations with Silverlight
# November 10, 2008 4:43 AM

Christopher Bennage said:

@Matthew thanks for the link, but I couldn't find the source for the Silverlight example. Just all the Papervision articles.

Any more info/advice is appreciated.

I've actually rewritten the example in this post to _not_ use storyboards, but to handle the animation manually.

# November 10, 2008 8:27 AM

T said:

Would be nice to see your best practice example. Any chance in updating with a new code example that uses manual animation?
# November 18, 2008 3:06 PM

Christopher Bennage said:

@ T I am planning to follow this up. I've been doing a lot of work/testing along these lines. I expect to post something end of this week, maybe the beginning of next.

# November 18, 2008 3:12 PM

About Christopher Bennage

Christopher is a software developer and consultant at Blue Spire Consulting, a company he co-founded with Rob Eisenberg in 2006. He is a Christian, a marginal musician, and an armchair philosopher. His interests include programming, liberal education, science, truth, beauty, and a number of deceased British authors (C. S. Lewis, G. K. Chesterton, and most recently Owen Barfield.) He lives in Tallahassee, FL with his wife and three children and still prefers to play as the Night Elves in WarCraft 3. Check out Devlicio.us!

Our Sponsors

Proudly Partnered With