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.

Published Tuesday, May 15, 2007 7:52 PM by Bill Reiss

Comments

# re: Creating a Game Loop

Excellent Tutorials! Thank you.

With the game loop. In the code, this line:

ship.RotationAngle++;  

updates the rotation of the object. It seems strange to me that i happens as slow as it does (roughly 6 seconds for the 360 rotation). What controls the clock, or speed? I played with the 'SpeedRatio' property of the Storyboard, but neither increasing or decreasing it had any effect.

Thanks...

Saturday, September 22, 2007 4:20 PM by Roger

# re: Creating a Game Loop

Excellent demo!!! I have not come across anything this usefull on Silverlight. Thank you.

Saturday, September 22, 2007 5:59 PM by Roger

# re: Creating a Game Loop

Yes in this demo the frame rate is 62 frames per second, so you're tied to that frame rate for the updates per second. In some of the follow up tutorials I get into how to calculate the elapsed time since the last update and use that in the calculations.

So in this one, if you wanted it to rotate faster, you could do a ship.RotationAngle+=2 or whatever number you want.

Glad you like the tutorials so far!

Bill

Saturday, September 22, 2007 8:47 PM by Bill Reiss

# re: Creating a Game Loop

Correction:

Original says "in the constructor, after the InitializeComponent()"

Should read "in the Page_Loaded event handler, after the InitializeComponent()"

Thanks for the great tutorial!

Tuesday, November 27, 2007 6:51 PM by DoomGoober