Tuesday, June 23, 2009
Death of TurboSprite
Earlier I began porting my object-oriented TurboSprite framework to WPF. This was relatively easy because WPF had the OnRender event and the DrawingContext that corresponds to WinForms Paint/Graphics combo.
When I decided to target Silverlight as the next platform for Silicon Commander Games development, one of the first hurdles was the fact that it did not expose the OnRender/DrawingContext feature of WPF. This forced me to use the animation system(s) built into Silverlight/WPF and abandon further development of TurboSprite.
One of the benefits of encapsulating animation in TurboSprite was its clean, object-oriented design. I could create animatable sprite classes that were self-contained and re-usable.
In Silverlight I am following the same philosphy of design, but using the animation model(s) provided by the framework. Note the plural!
Model 1 - Property Based Animation
This model works by creating Storyboard objects, and then adding animations (like DoubleAnimation, ColorAnimation, etc.) to its Children. You call Begin() to start animating, and can handle the Complete event when the animation is finished. You provide a Duration that controls how long the animation lasts. The Storyboard can have multiple animations in its Children collection. Each animation targets a specific property of a specific object.
I use this method when I need to move an object from one cell to another on the game grid. To simplify the model, I created a base class called SquareGridGameBoard that derives from UserControl. SquareGridGameBoard allows you to set the number of cells that compose the game board, as well as the width and height of each cell. It also provides a visual cell cursor, and a selection band that facilitates selecting a range of cells. SquareGridGameBoard also contains helper methods that make it easy to position, and move, an element on the game board.
The MoveElement helper method encapsulates the plumbing of creating a Storyboard, the animations, and handling the completed event. Here's the code:
//Move an element to another cell
public void MoveElement(UIElement element, int newX, int newY, Duration duration)
//Create Storyboard, and remember the element it references
Storyboard sb = new Storyboard();
sb.Completed += MoveCompleted;
_storyboardTargets[sb] = element;
//Animate X coordinate
DoubleAnimation animX = new DoubleAnimation();
animX.To = newX * CellWidth + _cellWidthHalf;
animX.Duration = duration;
Storyboard.SetTargetProperty(animX, new PropertyPath("(Canvas.Left)"));
//Animate Y coordinate
DoubleAnimation animY = new DoubleAnimation();
animY.To = newY * CellHeight + _cellHeightHalf;
animY.Duration = duration;
Storyboard.SetTargetProperty(animY, new PropertyPath("(Canvas.Top)"));
private void MoveCompleted(object sender, EventArgs e)
//what element was being moved?
Storyboard sb = sender as Storyboard;
UIElement element = _storyboardTargets[sb];
//push values into properties after animation changes them
double x = Canvas.GetLeft(element);
double y = Canvas.GetTop(element);
You can see this in action in the current demo as of Build 7. When you click on a cell of the game map, it creates a random StarShip, but also moves a StarSystem to that location, using the MoveElement method described above.
Model 2 - Frame-Based Animation
Another animation option that Silverlight provides is enabled by creating an event handler for the static CompositionTarget.Rendering event. When you do this, Silverlight calls your handler on each frame of animation. The default frame rate is 60 FPS, but this is adjustable by adding the "maxFramerate" parameter in your HTML.
I use this method to cycle the colors of an array of 10 SolidColorBrushes that draw the Shields around StarSystems. The Ellipse elements that compose the Shields have their Stroke properties set to one of the brushes in this static array. When the color of one of the brushes changes, it impacts all of the Shield Ellipses that used that brush in their Strokes.
You can see the Shield color cycling in action in Build 7 of the demo.
Sometimes it makes sense to use property-based animations, but sometimes frame-rate animations are a more logical choice. There's no need to sacrifice, use both models, and select the one that makes the most sense for each particular animation task in your Silverlight app!