Introduction
While leisurely browsing the net I came across a flash game called worm race game. As name suggests, the worm race is about a race between worms. The player is supposed to select one of the available worms and place a bet on it, then the race begins and if the winner is the worm the player selected then the player wins otherwise he loses. Somehow I felt it was a good game to learn animations in WPF by developing this game. So I attempted to write a similar game using animations in WPF.
- Making a sprite for the worm
- Animating the body of the worm to make it appear to be crawling
- Making a sprite into a user control
- Animating the worm from the start point to the end point
- A panel to place bets
- A notification window that shows what to do next and the results of the game
- Logic to select worms and decide winners
In the process of developing this game, I used and learned the following concepts in WPF:
- Making a UserConrol from a collection of controls
- Adding new/custom properties to a usercontrol
- Property animations and keyframeanimations in WPF
- Control Templates
- Styles
The game looks as in the following figure:

Sprites could be made by taking bitmaps (raster based) or drawing them ourselves (vector based). While sprites based on bitmaps are more realistic, they are relatively harder to implement and consume more resources than simple vector-based drawings and I wanted to use property animations of WPF so I chose vector-based sprites, simply made of some circles.

Animating the body of the worm to make it appear to be crawling
<DoubleAnimationUsingKeyFramesStoryboard.TargetProperty=
"(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"
Storyboard.TargetName="ellipse">
<EasingDoubleKeyFrameKeyTime="0"Value="0"/>
<EasingDoubleKeyFrameKeyTime="0:0:1"Value="-1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFramesStoryboard.TargetProperty=
"(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)"
Storyboard.TargetName="ellipse">
<EasingDoubleKeyFrameKeyTime="0"Value="0"/>
<EasingDoubleKeyFrameKeyTime="0:0:1"Value="-13"/>
</DoubleAnimationUsingKeyFrames>

4. Making a sprite into a usercontrol
public static readonly DependencyProperty WormColorProperty = DependencyProperty.Register("WormColor", typeof(Brush), typeof(Worm), new
FrameworkPropertyMetadata(new PropertyChangedCallback(OnWormColorChanged)));
- The Name of the property: "WormColor"
- Type of data held by the property: typeof(Brush)
- The Name of the class registering the property: typeof(Worm)
- Additional things about the property, including a callback: FrameworkPropertyMetadata(new PropertyChangedCallback(OnWormColorChanged))
Even though all we want is to register a callback that needs to be called when the property value changes, we must do it using a "FrameworkPropertyMetadata" instance only and there is no direct way of registering it.
B. Implementing dependency property
public Brush WormColor
{
get { return (Brush)GetValue(WormColorProperty); }
set { SetValue(WormColorProperty,value); }
}
The GetValue and SetValue references the dictionary of property values held by the object. The key is the dependency property we registered
private static void OnWormColorChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Worm wrm = (Worm)sender;
Brush b = wrm.WormColor;
wrm.ellipse.Fill = b;
wrm.ellipse1.Fill = b;
wrm.ellipse3.Fill = b;
wrm.ellipse4.Fill = b;
}

<CanvasGrid.Row="1">
<local:Wormx:Name="YellowWorm"WormColor="Yellow"/>
</Canvas>
Once we have a canvas, we can use simple property-based animation to move the worm from start the point to the end point.
- It must be a dependency property.
- It must belong to a class that inherits from DependencyObject and implements the IAnimatable interface.
- There must be a compatible animation data type available.
This means that to animate a dependency property, you need to have an animation class that supports its data type. For example, the Width property of a rectangle uses the double data type. To animate it, you use the DoubleAnimation class. However, the Color property of a SolidColorBrush object uses the color structure, so it requires the ColorAnimation class. If WPF doesn't provide the data type you want to use, you can create your own animation class for that data type.
Property-based animation and keyframe animation differ in the way they generate intermediate values of a property during a selected time interval. Property-based animation takes "From" and "To" values and uses linear interpolation to generate intermediate values and is smooth. On the other hand key frame animations change property values abruptly at specified times, and are often used when changing certain data types, such as a string.
Using property animation to move a worm
We need to move a worm horizontally along a canvas, so we need to change the "Canvas.Left" property of the worm from 0 to 750 (could be anything) over some random interval of time. "Canvas.Left" is a Double so we need to use "DoubleAnimation". The following code moves a worm from 0 to 750 somewhere between 10 and 15 seconds.
da =new DoubleAnimation(0, 750,TimeSpan.FromSeconds(rnd.Next(10, 15) % 15));
da.AccelerationRatio = rnd.NextDouble();
da.Completed += da_Completed;
da.Name ="YellowWorm";
YellowWorm.BeginAnimation(Canvas.LeftProperty, da);
- Duration: Sets the length of time the animation runs, from start to finish, as a Duration object (TimeSpan.FromSeconds(rnd.Next(10, 15) % 15))
- SpeedRatio: Increases or decreases the speed of the animation. The default value is 1
- AccelerationRatio and DecelerationRatio: Makes an animation nonlinear, so it starts off slow and then speeds up (by increasing AccelerationRatio) or slows down at the end (by increasing the DecelerationRatio). Both values are set from 0 to 1
- AutoReverse: If this is set to true, the animation will play out in reverse once it is complete, reversing to the original value
- FillBehavior: Determines what happens when the animation finishes. Usually, it keeps the property fixed at the ending value
- RepeatBehavior: Allows you to repeat an animation a specific number of times.
Of these we have used only AccelerationRatio so that worms show some random behavior during the course of race. In other words a worm may start fast initially bit slow down after a while, which makes the race a little more interesting.
The top panel of the grid is reserved for the start and the row of buttons to place various amounts of bets. Round buttons look better than rectangles (inspired from gold coins). We can make buttons round by changing the control template of the button and using a style to apply it for all buttons. We can also use triggers to generate some animations when the mouse hovers on buttons and the user clicks on it.
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<!--Border BorderThickness="4" BorderBrush="DarkGray" CornerRadius="10"-->
<Rectangle x:Name="mainButton" Fill="{TemplateBinding Background}" Stroke="DarkGray" StrokeThickness="5" RadiusX="25" RadiusY="25">
</Rectangle>
<!--/Border-->
<ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Rectangle x:Name="buttonHoverButton" Opacity="0" RadiusX="25" RadiusY="25">
<Rectangle.Fill>
<RadialGradientBrush>
<GradientStop Color="Transparent" Offset="1"/>
<GradientStop Color="Indigo" Offset="0.9"/>
<GradientStop Color="Blue" Offset="0.8"/>
<GradientStop Color="LawnGreen" Offset="0.7"/>
<GradientStop Color="Yellow" Offset="0.6"/>
<GradientStop Color="Orange" Offset="0.5"/>
<GradientStop Color="Violet" Offset="0.4"/>
<GradientStop Color="Transparent" Offset="0"/>
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="BitmapEffect">
<Setter.Value>
<OuterGlowBitmapEffect GlowColor="Violet" GlowSize="10"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="Button.IsPressed" Value="True" >
<Setter TargetName="mainButton" Property="Fill" Value="Gold"/>
</Trigger>
<EventTrigger RoutedEvent="Button.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="buttonHoverButton" Storyboard.TargetProperty="Opacity" To="1"
Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Button.MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="buttonHoverButton"
Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Logic to select worms and decide winners
Selecting Worm
We need to handle various mouse-related events of the worm for selecting a worm; they are:
- MouseEnter: this handler is invoked when the user hovers over a worm and here we make the transparent border of the worm visible so the highlighting rectangle appears around the worm and the details related to the worm will appear in the notification window.
- MouseLeave: it clears the border.
- MouseLeft Down : this will note the selected worm and moves the game state from "selectworm" to "selectbet" so that the user can place a bet.

- hover the mouse over worms to get details of worms
- select the worm
- select the amount
- click start
- on completion of a race you will see results in the notification window
- start changes to Reset
- click on Reset to play another race
Any out of order actions generate error messages. I hope this article helps in getting an overview of WPF animations and provides some enjoyment while playing the game.

Srinivas BaravatulaPosted Feb 12, 2013, 3:20 PM
hey nice article, i was working with WPF about 8 months back, its amazing how quickly you can forget stuff. your article really helped me recall a lot of stuff. thank you :)