Creating a Game Loop
Source code for this tutorial: http://silverlightrocks.com/Community/files/folders/slg101_tutorials/entry13.aspx
XAML based technologies such as Silverlight or WPF have robust animation capabilities which can be used to create compelling interfaces. Tools such as Blend can be used to create some very sophisticated timeline animations. Games, however, have their own needs when it comes to animation. These needs often don't fall into a timeline based style. Typically games will employ what is called a game loop to periodically check the state of the game and act appropriately, including collision detection, input handling, etc.
In Silverlight 1.1 Alpha, the easiest way to set up a loop like this is to create a Storyboard of zero duration and then implement an event handler for the Storyboard.Completed event which will execute game logic and then call Storyboard.Begin() to kick off the next iteration of the game loop.
Breaking change for Silverlight 1.1 Alpha Refresh: All Storyboard objects now need to be named, even ones added dynamically via code.
This is pretty simple to implement in code, in our case let's create a Storyboard field named gameLoop in the Page class. Then in the constructor, after the InitializeComponent() statement, add the following:
gameLoop = new Storyboard();
gameLoop.SetValue<string>(Storyboard.NameProperty, "gameloop");
this.Resources.Add(gameLoop);
gameLoop.Completed += new EventHandler(gameLoop_Completed);
gameLoop.Begin();
and then add the gameLoop_Completed method as follows:
void gameLoop_Completed(object sender, EventArgs e)
{
// do your game loop processing here
gameLoop.Begin();
}
Ok great, so now gameLoop_Completed will fire on each event interval. Let's add some code to make the ship rotate. First we need to modify the XAML for the ship a bit. Modify the ship.xaml to look like this:
<Canvas xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="25"
Height="40" RenderTransformOrigin="0.5,0.5"
>
<Canvas.RenderTransform>
<RotateTransform x:Name="rotateTransform" Angle="0"/>
</Canvas.RenderTransform>
<Path Data="M0,38 L12,0,24,38,18,32,7,32z" Stroke="#FFFFFFFF" StrokeThickness="2"/>
</Canvas>
The new stuff in the XAML is the RenderTransformOrigin attribute and the Canvas.RenderTransform element. The RenderTransformOrigin attribute is specifying that the origin for any transformations is half of the width and half of the height.
The RotateTransform element is setting up a rotation that we can then access via code. Note the x:Name of rotateTransform. Naming an element in XAML makes it easy to reference that element from code. So now we need to modify the code the the ship.xaml.cs to hook up with this named element.
Controls in Silverlight load the XAML for the canvas associated with the control in their constructor using the InitializeFromXaml method. The InitializeFromXaml method returns a FrameworkElement which points to the Canvas at the root of the control. To find elements that are children of this canvas, we need to store off the FrameworkElement that is returned from the InitializeFromXaml method and then call its methods to get the children. So to have someplace to store this FrameworkElement, create a new field in the Ship class of type FrameworkElement named root. Then change the InitializeFromXaml call to look like this:
root = this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd());
So now we have a reference to the Canvas that we can use to get at the children. Since we named the RotateTransform element, we can do this:
rotateTransform = root.FindName("rotateTransform") as RotateTransform;
You'll need to add a field of type RotateTransform named rotateTransform to the Ship class. Now for an easy way to access the rotation angle of the ship. Add a public property called RotationAngle:
public double RotationAngle
{
get { return rotateTransform.Angle; }
set { rotateTransform.Angle = value; }
}
And now from the game loop, before the Begin() statement, add code to increment the rotation angle:
ship.RotationAngle++;
And now if you run the program, you should see the ship rotating. This could also have been done with a Storyboard which executed for a certain duration and changed the angle from 0 to 360 and the repeated forever, but in our case, we are going to need tighter control over the rotation of the ship since soon we will be rotating the ship based on keyboard input.