Silverlight Games 101

Write games in Silverlight 2 using C# by Bill Reiss
Our upcoming Silverlight book for beginners (includes a great chapter on game development in Silverlight!) Hello! Silverlight 2 with Dave Campbell, available online now!



Pages

    Recent posts

    Navigation

    Archive

    Blogroll

      Tampa Divorce Lawyer

      North of Tampa in Lutz, Florida. A Tampa Divorce Lawyer focusing on family, divorce, and real estate law.

      Disclaimer

      The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

      Creating a Game Loop Part 1

      June 9, 2008 - Updated for Silverlight 2 Beta 2

      Source code for this tutorial: http://silverlightrocks.com/cs/files/folders/silverlight_2_tutorials_source_code/entry317.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.

      This has gotten a bit more interesting with Silverlight 2. There are now two ways to implement this, and I can't say yet which is better. I'm very interested to have you try each method out, and see which works better for you. I have a feeling it may fall somewhere in between and using a combination of both may be the best option.

      Technique 1: Using an Empty Storyboard

      Storyboards are the basis for time based animations in Silverlight, and typically contain one or more animation elements. There is, however, a little trick that can be done with Storyboards which is actually borrowed from the Flash guys. If you create a Storyboard with a duration of 0, it will fire its Completed event one frame after it is begun. So if you begin the Storyboard again in the Completed event, you'll have a timer that executes once per frame.

      NOTE: You cannot begin a Storyboard in a control (the Page is just a UserControl like any other) until the control is loaded. Therefore, we'll put the code to create the Storyboard in the Loaded event of the Page.  

      NEW FOR BETA 2! You can now begin Storyboards in the constructor, which can make code a bit simpler.

      First let's create a Storyboard field named gameLoop in the Page class.

         1: Storyboard gameLoop;

      Then in the constructor, after the InitializeComponent() statement, add the following:

      gameLoop = new Storyboard();
      gameLoop.SetValue(FrameworkElement.NameProperty, "gameloop");
      this.Resources.Add("gameloop", gameLoop);
      gameLoop.Completed += new EventHandler(gameLoop_Completed);
      gameLoop.Begin();

       

      Then we need to implement the gameLoop_Completed event. Remember to begin the Storyboard again so that it fires every frame.

      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:

         1: <UserControl x:Class="SpaceRocks.Ship"
         2:     xmlns="http://schemas.microsoft.com/client/2007" 
         3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
         4:     Width="26" Height="40" >
         5:     <Canvas x:Name="LayoutRoot" RenderTransformOrigin="0.5,0.5">
         6:         <Canvas.RenderTransform>
         7:             <RotateTransform x:Name="rotateTransform" Angle="0"/>
         8:         </Canvas.RenderTransform>
         9:         <Path Data="M0,38 L12,0,24,38,18,32,7,32z" Stroke="#FFFFFFFF" StrokeThickness="2"/>
        10:     </Canvas>
        11: </UserControl>

      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. At compile time, a partial class is created which contains the InitializeComponent method. In the InitializeComponent method, declarations and initializations are added for any element which has an x:Name attribute. If you go to the Ship's constructor, and right click on the InitializeComponent() method, and select "Go to Definition", you'll see this partial class. It should look something like this:

         1: public partial class Ship : System.Windows.Controls.UserControl {
         2:     
         3:     internal System.Windows.Controls.Canvas LayoutRoot;
         4:     
         5:     internal System.Windows.Media.RotateTransform rotateTransform;
         6:     
         7:     private bool _contentLoaded;
         8:     
         9:     /// <summary>
        10:     /// InitializeComponent
        11:     /// </summary>
        12:     [System.Diagnostics.DebuggerNonUserCodeAttribute()]
        13:     public void InitializeComponent() {
        14:         if (_contentLoaded) {
        15:             return;
        16:         }
        17:         _contentLoaded = true;
        18:         System.Windows.Application.LoadComponent(this, new System.Uri("/SpaceRocks;component/Ship.xaml", System.UriKind.Relative));
        19:         this.LayoutRoot = ((System.Windows.Controls.Canvas)(this.FindName("LayoutRoot")));
        20:         this.rotateTransform = ((System.Windows.Media.RotateTransform)(this.FindName("rotateTransform")));
        21:     }
        22: }

      Notice the rotateTransform field that was automatically generated for us based on the x:Name attribute we put on the RotateTransform. Now let's close that file and leave it alone, it's a generated file and if we make any changes to it we would lose them.

      To allow the Page object to get access to the rotateTransform field, let's create a public property on the Ship object called RotationAngle.

         1: public double RotationAngle
         2: {
         3:     get { return rotateTransform.Angle; }
         4:     set { rotateTransform.Angle = value; }
         5: }

      And now from the game loop, before the Begin() statement, add code to increment the rotation angle:

         1: void gameLoop_Completed(object sender, EventArgs e)
         2: {
         3:     // do your game loop processing here
         4:     ship.RotationAngle++;
         5:     gameLoop.Begin();
         6: }

      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.

      In the next post, we'll look at how to use a DispatcherTimer to do a game loop.

      Share this post : digg it! dotnetkicks it! technorati!
      Posted: Mar 13 2008, 21:07 by Bill Reiss | Comments (9) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      Comments

      Comments are closed