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.

      A Better Game Loop

      Source code: http://silverlightrocks.com/Community/files/folders/slg101_tutorials/entry21.aspx

      While working on my game, I found that especially when in full screen mode, if there are a lot of things in motion, the frames per second can decrease significantly. Now some of that I'm sure is because it is Alpha code and contains tons of debugging information. Still, in general, it is good to base your updates on the time since the last update, that way, your game will run at a consistent speed even if the frame rate is slower on a particular system, or based on how much you have going on.

      So to improve our game loop, we can keep track of when we last called Storyboard.Begin and then when Completed fires, we can subtract this from the current time to get our elapsed time.

      To make things a bit more reusable, we can do like we did with the KeyHandler class and create a new GameLoop class in the SLG101Utilities library.

      Breaking Change: Code updated for breaking change in Silverlight 1.1 Alpha refresh where all storyboards now need to be named.

      The GameLoop class will look like this:

       

      using System;
      using System.Windows;
      using System.Windows.Controls;
      using System.Windows.Documents;
      using System.Windows.Ink;
      using System.Windows.Input;
      using System.Windows.Media;
      using System.Windows.Media.Animation;
      using System.Windows.Shapes;
      
      namespace SilverlightGames101.Utilities
      {
          public class GameLoop
          {
              Canvas targetCanvas = null;
              Storyboard storyboard;
              DateTime lastUpdateTime = DateTime.MinValue;
              TimeSpan elapsedTime;
              public delegate void UpdateDelegate(TimeSpan ElapsedTime);
              public event UpdateDelegate Update;
              public void Attach(Canvas canvas)
              {
                  targetCanvas = canvas;
                  storyboard = new Storyboard();
      storyboard.SetValue<string>(Storyboard.NameProperty, "gameloop"); canvas.Resources.Add(storyboard); lastUpdateTime = DateTime.Now; storyboard.Completed += new EventHandler(storyboard_Completed); storyboard.Begin(); } public void Detach(Canvas canvas) { storyboard.Stop(); canvas.Resources.Remove(storyboard); } void storyboard_Completed(object sender, EventArgs e) { elapsedTime = DateTime.Now - lastUpdateTime; lastUpdateTime = DateTime.Now; if (Update!=null) Update(elapsedTime); storyboard.Begin(); } } }

      And the GameLoop object will fire an Update event which can then be handled by the game canvas.

      We need to make some changes to the Page class to use this GameLoop instead of the one it was using. First, change the declaration of the gameLoop field to look like this:

      GameLoop gameLoop = new GameLoop();
      

      Then remove the gameLoop related code from the Page_Loaded method and replace it with the following:

      gameLoop.Attach(this);
      gameLoop.Update += new GameLoop.UpdateDelegate(gameLoop_Update);
      

      And then, replace the gameLoop_Completed method with the following:

      void gameLoop_Update(TimeSpan ElapsedTime)
      {
          if (keyHandler.IsKeyPressed(Key.A))
          {
              ship.RotationAngle -= rotationSpeed * ElapsedTime.TotalSeconds;
          }
          if (keyHandler.IsKeyPressed(Key.D))
          {
              ship.RotationAngle += rotationSpeed * ElapsedTime.TotalSeconds;
          }
      }
      

      And since we are now multiplying the rotationSpeed by the total seconds since the last update, and the frame rate is about 60 frames per second, change the rotationSpeed field's value from 3 to 180. This will give us a rotation speed about the same as what we had before.

      So this is a bit cleaner and a lot more reusable, and it gives the added benefit of keeping the game moving at a constant speed.

      Posted: May 21 2007, 14:04 by Bill Reiss | Comments (8) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      Keyboard Input

      Source code for this tutorial: http://silverlightrocks.com/Community/files/folders/slg101_tutorials/entry17.aspx

      Now that we can rotate the ship using the game loop, let's add a way to control this rotation using the keyboard. You main forms of human input in a Silverlight application will typically be the keyboard or the mouse, I'll write a tutorial on mouse input in the future, but the mouse buttons generally behave like the keyboard buttons, and are actually a bit simpler.

      Introducing SLG101Utilities

      As I progress through these tutorials, there will be classes and code that are not specific to this particular game, and to make it easier to take advantage of them in your own games, I have added a SLG101Utilities project to the main game project. Eventually I'll make this available separately but for now since it will change a lot, it will be packaged with the SpaceRocks solution.

      Keyboard Overview

      Since Silverlight is a cross platform framework, keyboard handling is a bit different than what you may be used to. The KeyDown and KeyUp events pass a KeyboardEventArgs object which contains a Key and a PlatformKey field. The Key field contains the common keys to both Windows and the Mac and the values for each key are listed here:

      http://msdn2.microsoft.com/en-us/library/bb232871.aspx

      Since you typically would want your browser-based game to work on as many platforms as possible without custom code, you'll probably stick to using the Key field and ignore the PlatformKey field.

      NOTE: The following will probably change before 1.1 goes to release.

      There currently isn't a Key enum defined in Silverlight 1.1 Alpha, so I have defined my own in SLG101Utilities, using the same values specified in the URL above. I have defined this key enum in Key.cs.

      Trapping Keyboard Events

      Keyboard handling in Silverlight 1.1 Alpha consists of handling the KeyDown and KeyUp events on the root canvas. This is the canvas that is associated with the Page class in SpaceRocks (since we didn't rename the PAge class after the Orcas new project wizard generated it for us). Adding event listeners to other Canvas objects will cause errors.

      To make things easier from a game development standpoint, it's typically easier to poll whether a key is pressed during the game loop than to handle the key events and set states appropriately. So in SLG101Utilities, I have added a KeyHandler class which makes this simple. Here is the source code for the KeyHandler class:

          public class KeyHandler
          {
              bool[] isPressed = new bool[255];
              Canvas targetCanvas = null;
              public void ClearKeyPresses()
              {
                  for (int i = 0; i < 255; i++)
                  {
                      isPressed[i] = false;
                  }
              }
      
              public void Attach(Canvas target)
              {
                  ClearKeyPresses();
                  targetCanvas = target;
                  target.KeyDown += new KeyboardEventHandler(target_KeyDown);
                  target.KeyUp += new KeyboardEventHandler(target_KeyUp);
                  target.LostFocus += new EventHandler(target_LostFocus);
              }
      
              public void Detach(Canvas target)
              {
                  target.KeyDown -= new KeyboardEventHandler(target_KeyDown);
                  target.KeyUp -= new KeyboardEventHandler(target_KeyUp);
                  target.LostFocus -= new EventHandler(target_LostFocus);
                  ClearKeyPresses();
              }
      
              void target_KeyDown(object sender, KeyboardEventArgs e)
              {
                  isPressed[e.Key] = true;
              }
      
              void target_KeyUp(object sender, KeyboardEventArgs e)
              {
                  isPressed[e.Key] = false;
              }
                  
              void target_LostFocus(object sender, EventArgs e)
              {
                  ClearKeyPresses();            
              }
      
      
              public bool IsKeyPressed(Key k)
              {
                  int v = (int)k;
                  if (v < 0 || v > 82) return false;
                  return isPressed[v];
              }
      
          }
      

      Basically how it works is that you call the Attach() method of the KeyHandler object passing in your root canvas object. This Attach method wires up the events for your root canvas so that they are handled by the KeyHandler object. Then in your game loop, you would call the IsKeyPressed() method for each key that you care about. The LostFocus event is important since if a key is being pressed when you lose focus, you won't see the KeyUp event and the key will get "stuck" in the down position. So on LostFocus, KeyHandler clears all of the currently pressed keys.

      So now to use this in the SpaceRocks game.

      The namespace for the SLG101Utilities library is SilverlightGames101.Utilities. So first at the top of the Page.xaml.cs file, let's add a using statement for easier access to the KeyHandler class:

      using SilverlightGames101.Utilities;
      Now declare a KeyHandler field in the Page class
      KeyHandler keyHandler = new KeyHandler();

      In Page_Loaded, we need to Attach to the keyHandler, like this:

      keyHandler.Attach(this);

      and now all that's left is to check which keys are pressed in the game loop. For this game, we'll use the standard left, right, up keys that a lot of games use, where W=Thrust, A=Left, D=Right, and Space=Fire. For now, we'll just handle the Left and Right keys. So remove the test code from the last tutorial from the gameLoop_Completed method (the ship.RotationAngle++ statement) and replace it with the following:

      if (keyHandler.IsKeyPressed(Key.A))
      {
          ship.RotationAngle -= rotationSpeed;
      }
      if (keyHandler.IsKeyPressed(Key.D))
      {
          ship.RotationAngle += rotationSpeed;
      }
      

      If the A key is pressed, we'll subtract rotationSpeed from the rotation angle, and if D is pressed, we'll add the rotationSpeed. Define rotationSpeed as a field in the Page class, as follows:

      float rotationSpeed = 3;

      You can adjust this value to change the speed of rotation, but it seems to be about right.

      We're done for now, you should now be able to control the rotation of the ship using the A and D keys.

      Posted: May 20 2007, 06:21 by Bill Reiss | Comments (8) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under: Games

      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.

      Posted: May 15 2007, 14:52 by Bill Reiss | Comments (8) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      Drawing a Sprite

      Source code for this completed tutorial: http://silverlightrocks.com/Community/files/folders/slg101_tutorials/entry10.aspx

      In game programming, 2D elements that are drawn to the screen are commonly called sprites. Sprites are the building block upon which most 2D games are based, and so it makes sense that we cover it first. Unless you're doing a completely vector-based game, you'll have to deal with sprites at some point during your game development.

      For this game, we need a Ship sprite. So add a new item to the SpaceRocks project of type "Silverlight User Control" and name it "Ship". Then replace the contents of Ship.xaml with the following:

      <Canvas xmlns="http://schemas.microsoft.com/client/2007" 
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
              Width="25"
              Height="40"
              >
        <Path Data="M0,38 L12,0,24,38,18,32,7,32z" Stroke="#FFFFFFFF" StrokeThickness="2"/>
      </Canvas>

      The Path element allows you to draw vector graphics using a concise format. It allows for lines and curves (bezier and arcs), and can optionally be filled. You can specify a brush for the stroke and the fill. More information on the format of the Data attribute here:

      http://msdn2.microsoft.com/en-us/library/ms752293.aspx

      In this example, the M command moves the current drawing position to (0,38) then the L command specifies a polyline to (12,0),(24,38),(18,32),(7,32), and then the "z" connects the polyline back to the original point of (0,38).

      The Stroke attribute consists of 4 bytes of data, the first being the alpha channel for transparency and the other 3 bytes being red, green, and blue values respectively. In this case, we will be drawing fully opaque with a white solid brush. The StrokeThickness attribute specifies that the lines will have a thickness of 2 pixels.

      So now to add the ship sprite to the game surface. There are two main ways you can put a child object in a canvas. You can either declare it in the XAML for the parent object, or you can create it in code and add it to the game surface. Creating the objects in code and dynamically adding them to the game surface is very useful when your game needs to add and remove sprites from the game surface on the fly, or if you don't know how many objects of a certain type you are going to have ahead of time. In a future tutorial we will create some objects in code dynamically. In this case, since this game will need one and only one ship, we can add it to the XAML.

      Change the page.xaml file to look like this:

      <Canvas
              xmlns="http://schemas.microsoft.com/client/2007" 
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
              xmlns:SpaceRocks="clr-namespace:SpaceRocks;assembly=ClientBin/SpaceRocks.dll"
              x:Name="parentCanvas" 
              Loaded="Page_Loaded"
              x:Class="SpaceRocks.Page;assembly=ClientBin/SpaceRocks.dll"
              Width="640"
              Height="480"
              Background="Black"
              >
      <SpaceRocks:Ship x:Name="ship" Width="30" Height="40" Canvas.Left="305" Canvas.Top="220"/>
      </Canvas>

      Now if you go ahead an run the program, you should get a good idea of what game we're creating here.

      Notice the Canvas.Left and Canvas.Top attributes. This is how absolute positioning is done in XAML. These attributes will become important as we start animating the ship. We'll start getting into that in the next tutorial.

      Posted: May 14 2007, 22:11 by Bill Reiss | Comments (2) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under: Games

      Getting Started

      NOTE: This post is obsolete. Please go to this link for more info:

      http://silverlightrocks.com/community/blogs/silverlight_games_101/archive/2008/03/12/back-to-the-beginning-sorta.aspx 

      Source code for this tutorial: http://silverlightrocks.com/Community/files/folders/slg101_tutorials/entry8.aspx

      Ok so now if you have set up your environment by installing everything listed in the last post, we should be ready to create a Silverlight Project.

      In Visual Studio "Orcas", create a new C# project of type "Silverlight Project". Call it "SpaceRocks". This project will be the basis for the first set of tutorials we'll go through.

      Now run your project. If all goes well, one of two things will happen.

      Outcome 1: You will get prompted with a "Get Silverlight Alpha" control. Go ahead and install Silverlight 1.1 Alpha by clicking on the control.

      Outcome 2: You'll get a blank page.

      Ok so by now you should be seeing a blank page. Why a blank page? Well the default background color for a new Silverlight project is White. If you look at your Page.xaml file, it will look like this:

       

      <Canvas x:Name="parentCanvas"
              xmlns="http://schemas.microsoft.com/client/2007" 
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
              Loaded="Page_Loaded" 
              x:Class="SpaceRocks.Page;assembly=ClientBin/SpaceRocks.dll"
              Width="640"
              Height="480"
              Background="White"
              >
      
      </Canvas>

      XAML files are used for forms layout, similar to HTML for a web page. Typically in Silverlight you will lay out your page and controls using XAML and then use code behind to handle the events, kick off animations, etc. Visual Studio "Orcas" does not currently have designer support for Silverlight controls and pages, but if you right click on the file in Solution Explorer, there is an "Open in Expression Blend..." option. Expression Blend is a tool which allows you to lay out XAML based controls, add animations, and more. 

      Back to Visual Studio...change the Background attribute to "Black" and run it again. Now you should have a black box 640x480 inside the browser.

      Posted: May 11 2007, 09:39 by Bill Reiss | Comments (7) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      My Development Environment for Silverlight

      My current development environment for Silverlight 1.1 is as follows:

      • Microsoft Visual Studio Codename "Orcas" Beta 2
      • Microsoft Silverlight Tools Alpha for Visual Studio codename "Orcas" Beta 2
      • Microsoft Silverlight 1.1 Alpha Refresh for Windows
      • Expression Blend 2 August Preview

      There are links to each of these downloads on this page: http://silverlight.net/GetStarted/

      As you can tell, this stuff is all cutting edge and is all in prerelease, but it's the best that's out there right now for doing Silverlight 1.1 development. The good news is that you can get started with all of these technologies now for free and get a jump on everyone else doing Silverlight development.

      Posted: May 09 2007, 13:17 by Bill Reiss | Comments (10) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      Welcome to Silverlight Games 101

      On April 30, 2007, Microsoft announced Silverlight 1.1 which I feel will change web development forever. Silverlight 1.1 allows developers to create rich cross-browser and cross-platform web applications using managed code such as C#.

      This blog will focus on topics related to developing Silverlight games in managed code. Since I am most comfortable with C#, all sample code will be in C# format, but it could be written in any managed language including VB.Net and even IronPython.

      Even though we will focus on game development here, many of the concepts will be useful to non-gaming applications.

      Please be aware that Silverlight 1.1 is currently an Alpha release meaning interfaces, class definitions, and pretty much anything else can change before the official release. I will do my best to make sure that the code samples here remain up to datw with the latest version of Silverlight.

      Bill Reiss

      Posted: May 09 2007, 11:48 by Bill Reiss | Comments (3) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under: Games