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.

      Addicted to Silverlight? There's still hope...

      Try some Silverlight Rehab, it looks like Microsoft is giving back to the community by providing help for the addicts. You can see some candid video here:

      http://on10.net/blogs/tina/Silverlight-Rehab/

      Posted: Mar 31 2008, 12:24 by Bill Reiss | Comments (1) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      The Microsoft MVP Summit 2008 starts 2 weeks from today

      The Microsoft MVP Summit is a yearly conference where MVPs from around the world come to Seattle and Redmond and meet with other MVPs and Microsoft employees, including some of the people on the product teams that they interact with. I'm fortunate to be able to attend this year, and I look forward to meeting people that I've seen around the same online haunts that I have.

      I have officially been switched over from a XNA/DirectX MVP to a Client App Dev MVP which is where the Silverlight and WPF MVPs are currently being put. I am still interested and somewhat active in the XNA community, but I have been spending more of my time on the Silverlight side of things so it made sense to make the switch official. So that means I'll be hanging out during the day with the other Silverlight focused MVPs and the Silverlight team, so I'm pretty excited about that.

      So if you're going, try to seek me out, I'd love to meet you. I'll be wearing a Blue Rose Games T-shirt some of the time which should help you recognize me.

      Posted: Mar 31 2008, 07:31 by Bill Reiss | Comments (1) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      Keyboard Input for Silverlight 2 Games

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

      Trapping Keyboard Events

      Keyboard handling in Silverlight 2 consists of handling the KeyDown and KeyUp events on a FrameworkElement that has focus. The needs of a game are typically a bit different than regular text input since what we're really interested in is whether a key is currently pressed or not.

      To help with this, I have added another class to the BlueRoseGames.Helpers library.

      I've got some comments about this, so let me elaborate. If you handle the KeyDown events yourself and move the sprite based on these events, you're at the mercy of the key repeat timer to move your sprite. This means that it will move once immediately, and then delay a bit before moving again, and once you hit the rapid repeat time, it will move quicker. This is probably desired for some games, but for most games, you're probably more interested in starting movement when you press a key, and ending that movement when the key is released. That's what this helper class is designed to do for you.

      This class in the BlueRoseGames.Helpers.Keyboard.KeyHandler class and it keeps track of the KeyDown and KeyUp events so that all the caller has to do is call a IsKeyPressed method to see if a key is currently pressed.

      Here is the source code for the KeyHandler class:

      using System.Windows;
      using System.Windows.Input;
      using System.Collections.Generic;
       
      namespace BlueRoseGames.Helpers.Keyboard
      {
          public class KeyHandler
          {
              Dictionary<Key, bool> isPressed = new Dictionary<Key, bool>();
              FrameworkElement targetElement = null;
              public void ClearKeyPresses()
              {
                  isPressed.Clear();
              }
       
              public KeyHandler(FrameworkElement target) 
              { 
                  ClearKeyPresses(); 
                  targetElement = target; 
                  target.KeyDown += new KeyEventHandler(target_KeyDown);
                  target.KeyUp += new KeyEventHandler(target_KeyUp); 
                  target.LostFocus += new RoutedEventHandler(target_LostFocus);
              }         
              
              void target_KeyDown(object sender, KeyEventArgs e) 
              { 
                  if (!isPressed.ContainsKey(e.Key)) 
                  { 
                      isPressed.Add(e.Key, true); 
                  } 
              }         
              
              void target_KeyUp(object sender, KeyEventArgs e) 
              { 
                  if (isPressed.ContainsKey(e.Key)) 
                  { 
                      isPressed.Remove(e.Key); 
                  } 
              }
              
              void target_LostFocus(object sender, RoutedEventArgs e) 
              { 
                  ClearKeyPresses(); 
              }         
              
              public bool IsKeyPressed(Key k) 
              { 
                  return isPressed.ContainsKey(k); 
              }
          }
      }

      Basically how it works is that the constructor accepts a FrameworkElement and the KeyUp and KeyDown events are wired up for that FrameworkElement. Then as keys are pressed and released, the current list of pressed keys is kept in the Dictionary. We're only really using the key of the dictionary and not the value. This gives us an indexed quick lookup of the keys that are currently pressed. 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 hardest part of this whole thing was getting focus to the Silverlight control. If the Silverlight control doesn't have focus, then you're not going to see the key presses. There have been some discussions on the forums lately about how to force focus to the control, and there doesn't seem to be a good way that works cross browser so we're stuck with making the user click on the control to make it get focus.

      First we need some text to indicate that the user has to click. We'll put this after the Canvas in the Page.xaml but still inside the Grid element:

      <TextBlock x:Name="clickToStart" Width="250" Height="80" Text="Click to Start!" Foreground="White" VerticalAlignment="Top" Margin="50" FontSize="35" TextAlignment="Center"/>

      The game should now look like this:

      clicktostart

      We'll need a using statement in the Page.xaml.cs so that we can access the KeyHandler class:

      using BlueRoseGames.Helpers.Keyboard;

      Now we won't start our timer until after the control has focus, otherwise the player won't be able to control the ship when the game starts. We'll also want to stop the timer if the control loses focus, which will pause the game. When the control receives focus, we'll want to hide the TextBlock that tells the user to click, and on a loss of focus, we'll display it again.

      One last thing we'll need to do is actually handle checking for which keys are pressed. We'll check for A or the Left Cursor Key to turn left, and D or the Right Cursor Key to turn right. This is the updated Page class in Page.xaml.cs:

      public partial class Page : UserControl
      {    
          StoryboardGameLoop gameLoop;    
          //   DispatcherTimerGameLoop gameLoop;    
          KeyHandler keyHandler;     
          double rotationSpeed = 150;    
          
          public Page()    
          {        
              InitializeComponent(); 
              this.GotFocus += new RoutedEventHandler(Page_GotFocus);
              this.LostFocus += new RoutedEventHandler(Page_LostFocus);
              this.MouseLeftButtonDown += new MouseButtonEventHandler(Page_MouseLeftButtonDown);
              gameLoop = new StoryboardGameLoop(this);
              //  gameLoop = new DispatcherTimerGameLoop();        
              gameLoop.Update += new GameLoop.UpdateHandler(gameLoop_Update);
              keyHandler = new KeyHandler(this);    
          }
       
          void gameLoop_Update(object sender, GameLoopUpdateEventArgs e)
          {
              // do your game loop processing here
              if (keyHandler.IsKeyPressed(Key.A) || keyHandler.IsKeyPressed(Key.Left))
              {
                  ship.RotationAngle -= rotationSpeed * e.Elapsed.TotalSeconds;
              }
              else if (keyHandler.IsKeyPressed(Key.D) || keyHandler.IsKeyPressed(Key.Right))
              {
                  ship.RotationAngle += rotationSpeed * e.Elapsed.TotalSeconds;
              }
          }     
          
          void Page_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)    
          {        
              clickToStart.Visibility = Visibility.Collapsed;
              gameLoop.Start();    
          }     
          
          void Page_LostFocus(object sender, RoutedEventArgs e)    
          {        
              clickToStart.Visibility = Visibility.Visible;
              gameLoop.Stop();
          }     
          
          void Page_GotFocus(object sender, RoutedEventArgs e)    
          {        
              clickToStart.Visibility = Visibility.Collapsed;
              gameLoop.Start();    
          }     
      }

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

      Posted: Mar 31 2008, 06:08 by Bill Reiss | Comments (20) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      Oops found a bug in the last tutorial

      The DispatcherTimerGameLoop had a problem where it started the timer immediately when it was created instead of waiting for the start event. The tutorial and the source code have been updated with this fix. You can find the updated source code here:

      http://silverlightrocks.com/cs/files/folders/silverlight_2_tutorials_source_code/entry337.aspx

      Posted: Mar 30 2008, 18:44 by Bill Reiss | Comments (3) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      Another Silverlight 2 Game Programming Blog

      I just came across a new Silverlight game programming blog at http://silverlight.net/blogs/msnow/archive/2008/03/25/introduction.aspx

      The author is Mike Snow who is a Microsoft employee on the Web Tools team. I think it's great whenever there's more sources for information out there, and I'm sure we'll be covering some different stuff or in a different way, so I look forward to seeing what he comes up with. Welcome, Mike!

      Posted: Mar 29 2008, 14:00 by Bill Reiss | Comments (1) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      The Start of a Game Helper Class Library

      Updated June 9, 2008 for Silverlight 2 Beta 2

      Note: This tutorial has been updated to fix a bug in the DispatcherTimerGameLoop class, where the timer would start upon creation instead of waiting for the Start() method.

      Source code: http://silverlightrocks.com/cs/files/folders/silverlight_2_tutorials_source_code/entry337.aspx

      So I did this with the old tutorials, and a little differently, this time I'm going to try to keep it a bit cleaner and to follow better namespacing and project layout, etc. One of the goals of this blog is to help you write games faster, and one way to do that is to give you some helper classes that can get you going and you can use in your own games. I have added a class library project to the SpaceRocks solution called BlueRoseGames.Helpers and when it makes sense, I'll add classes there so that you can use them in your own projects.

      Anything that I add to that class library or anything else I blog about here you are welcome to use and modify in your own games and redistribute freely. It is all provided "as is", and if you're using it for any other purpose than in your own applications (for example, if you wanted to redistribute it as part of your own library, etc) please contact me for permission.

      Ok so now that that's out of the way, the first thing I've added to the Helpers class library is the two game loop implementations (Storyboard and DispatcherTimer) that were covered in the Creating a Game Loop tutorials. They both inherit from an abstract GameLoop class and can be used easily in your own game. First the code...

      GameLoop.cs:

      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 BlueRoseGames.Helpers.Timers
      {
          public struct GameLoopUpdateEventArgs
          {
              public GameLoopUpdateEventArgs(TimeSpan elapsed)
              {
                  Elapsed = elapsed;
              }
       
              public TimeSpan Elapsed;
          }
       
          public abstract class GameLoop
          {
              protected DateTime lastTick;
              public delegate void UpdateHandler(object sender, GameLoopUpdateEventArgs e);
              public event UpdateHandler Update;
       
              public void Tick()
              {
                  DateTime now = DateTime.Now;
                  TimeSpan elapsed = now - lastTick;
                  lastTick = now;
                  if (Update != null) Update(this, new GameLoopUpdateEventArgs(elapsed));
              }
       
              public virtual void Start()
              {
                  lastTick = DateTime.Now;
              }
       
              public virtual void Stop()
              {
              }
          }
      }

      So what we have are a Start, Stop, and Tick method, and an Update event that provides the elapsed time since the last update. By providing this abstract class, it is easy to swap out the two timers for each other without changing other code.

      Now for the Storyboard technique. This class is called StoryboardGameLoop.

      StoryboardGameLoop.cs:

      using System;
      using System.Windows;
      using System.Windows.Media.Animation;
       
      namespace BlueRoseGames.Helpers.Timers
      {
          public class StoryboardGameLoop : GameLoop
          {
              bool stopped = true;
              Storyboard gameLoop = new Storyboard();
       
              public StoryboardGameLoop(FrameworkElement parent) : this(parent, 0)
              {
                  
              }
       
              public StoryboardGameLoop(FrameworkElement parent, double milliseconds)
              {
                  gameLoop.Duration = TimeSpan.FromMilliseconds(milliseconds);
                  gameLoop.SetValue(FrameworkElement.NameProperty, "gameloop");
                  parent.Resources.Add("gameloop", gameLoop);
                  gameLoop.Completed += new EventHandler(gameLoop_Completed);
              }
       
              public override void Start()
              {
                  stopped = false;
                  gameLoop.Begin();
                  base.Start();
              }
       
              public override void Stop()
              {
                  stopped = true;
                  base.Stop();
              }
       
              void gameLoop_Completed(object sender, EventArgs e)
              {
                  if (stopped) return;
                  base.Tick();
                  (sender as Storyboard).Begin();
              }
          }
      }

      The constructor takes a FrameworkElement, since Storyboards need to be added to the Resources collection of a FrameworkElement, and optionally a milliseconds between updates value. This will default to 0 if not specified.

      The DispatcherTimerGameLoop is similar.

      DispatcherTimerGameLoop.cs:

      using System;
      using System.Windows.Threading;
       
      namespace BlueRoseGames.Helpers.Timers
      {
          public class DispatcherTimerGameLoop : GameLoop
          {
              DispatcherTimer t = new DispatcherTimer();
              public DispatcherTimerGameLoop() : this(0)
              {
              }
       
              public DispatcherTimerGameLoop(double milliseconds)
              {
                  t.Interval = TimeSpan.FromMilliseconds(milliseconds);
                  t.Tick += new EventHandler(t_Tick);
              }
       
              public override void Start()
              {
                  t.Start();
                  base.Start();
              }
       
              public override void Stop()
              {
                  t.Stop();
                  base.Stop();
              }
       
              void t_Tick(object sender, EventArgs e)
              {
                  base.Tick();
              }
       
          }
      }

      Now to use these in our game. The Page class gets a lot simpler. First the constructor:

      public Page()
      {
          InitializeComponent();
      //     StoryboardGameLoop gameLoop = new StoryboardGameLoop(this);
          DispatcherTimerGameLoop gameLoop = new DispatcherTimerGameLoop();
          gameLoop.Update += new GameLoop.UpdateHandler(gameLoop_Update);
          gameLoop.Start();
      }

      Pretty straightforward stuff. We create our game loop object, wire up its Update event, and start the game loop. If you want to use a DispatcherTimer instead, comment out the game loop declaration and uncomment the other one.

      Then all that's left is to handle the Update event:

      void gameLoop_Update(object sender, GameLoopUpdateEventArgs e)
      {
          // do your game loop processing here
          ship.RotationAngle += 100 * e.Elapsed.TotalSeconds;
      }

      I went through this pretty quickly because the details about how the game loop works have already been covered, and this was basically a refactoring. I hope you get good use out of these helper classes and others that I'll add along the way.

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

      The DispatcherTimer Interval Property

      I've been playing around a bit more with the DispatcherTimer and found something interesting. If you set the Interval of the DispatcherTimer to 0, the Tick will fire once every 16 milliseconds, which is equal to the frame rate. So unless this is going to change, this is an easy way to duplicate the Storyboard Game Loop technique more cleanly. More in the next post as I create some Game Loop classes to make reuse easier.

      Posted: Mar 19 2008, 13:28 by Bill Reiss | Comments (1) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      I'm Speaking at the Sarasota .Net Developers Group on March 19

      As a ramp up for the Orlando Code Camp on Saturday, I'll be speaking in Sarasota 6-8 PM on March 19 at the Sarasota Community Foundation http://www.cfsarasota.org/ 

      I'll be speaking about the new features in Silverlight 2, should be a fun talk.

      The Sarasota .Net Developers site is at http://www.sarasotadev.net/ for more information.

      Posted: Mar 15 2008, 15:23 by Bill Reiss | Comments (3) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      Silverlight Sessions at Orlando Code Camp on March 22, 2008

      I just wanted to let everyone know that I'll be doing 1 (possibly 2) sessions on Silverlight 2 at the Orlando Code Camp next weekend. The site is http://www.orlandocodecamp.com/ and it's currently full, but I know usually if you show up the day of the event, there are enough no-shows that you can get in. I'll be presenting on some of the new features in Silverlight 2, and also giving an intro to Blend from a developer standpoint. Hope to see you there.

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

      Creating a Game Loop Part 2

      June 9, 2008 - Updated for Silverlight 2 Beta 2

      Source code here: http://silverlightrocks.com/cs/files/folders/silverlight_2_tutorials_source_code/entry324.aspx

      Ok so I got a few comments after the last post asking why I didn't use the new DispatcherTimer for my game loop. The answer is that that is what this post was going to be about, and it still is.

      I've done a bit more experimenting with the two different techniques (Storyboard or DispatcherTimer) and have come up with the following observations. I encourage you to do your own tests and see if you get the same results.

      The Storyboard timer seems to provide smoother animations, and I think this makes sense, since the repositioning of the sprites is in sync with the frame rate. The DispatcherTimer has no idea when the frames are being rendered, it's running in a different thread.

      UPDATE: Looks like if we set the timer interval to 0, then they behave about the same, executing once per frame.

      So why use a DispatcherTimer? Well from some limited testing, it seems that it is more efficient on the CPU, it's simpler to implement, and doesn't need to be bound to a FrameworkElement. Also, the DispatcherTimer technique is better supported in WPF, so if you are writing code that you want to use on both, you may want to use a DispatcherTimer.

      So let's see how the game loop looks with a DispatcherTimer.

      Technique #2: Using a DispatcherTimer for the game loop

      First let's make it easier for both types of timers to call the game loop logic. We can do that by creating an Update method in the Page class which we will put our update logic in. The updated gameLoop_Completed and Update methods are as follows:

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

      Now if you run the program, it should still act just as it did before. The DispatcherTimer class is in the System.Windows.Threading namespace, so let's add a using statement for that to the Page.xaml.cs file:

         1: using System.Windows.Threading;

      And then in the Page constructor, let's make it so that we can use either technique by changing a preprocessor directive. So we'll wrap the Storyboard code in a #if statement, and then put the DispatcherTimer code in a #else statement:

      public Page()
      {
          InitializeComponent();
      STORYBOARD_TIMER
          gameLoop = new Storyboard();
          gameLoop.SetValue(FrameworkElement.NameProperty, "gameloop");
          this.Resources.Add("gameloop", gameLoop);
          gameLoop.Completed += new EventHandler(gameLoop_Completed);
          gameLoop.Begin();
      #else
          DispatcherTimer t = new DispatcherTimer();
          t.Interval = TimeSpan.Zero;
          t.Tick += new EventHandler(t_Tick);
          t.Start();
      #endif
      }

      The Interval of the timer is 1000/60 milliseconds, or the time between frames if the frame rate is 60. You can change this value to be consistent with your frame rate.

      Now we need a t_Tick method to handle the Tick event, we'll simply call the Update method in there:

         1: void t_Tick(object sender, EventArgs e)
         2: {
         3:     Update();
         4: }
         5:  

      And now if you run the program, it should behave roughly the same as when we had a Storyboard timer. One difference is that at least when I run it, it rotates slower using this technique. It seems that the DispatcherTimer doesn't actually fire every 16.66667 milliseconds like it's supposed to, and I don't know if that is something they are going to improve, but this brings up a game programming technique that you should familiarize yourself with.

      Typically you should not rely on timers firing exactly when you expect them to unless you're using a timer that guarantees it will fire on a certain interval, since things can happen, like the machine is bogged down with other work, etc. If you go back now and play some of the early PC games on modern hardware, they will be unplayable because they are running so fast. This is because their timers were based on the CPU speed, and as processors got faster, there was nothing in their game to account for this.

      So how can we take care of this? If we keep track of the time since the last Update call, and then use this time difference in our calculations, then it won't matter if the timer fires once a second, the animation will be very choppy, but it will still run at the same speed.  First let's declare a DateTime field in the Page class to store the last update time:

         1: DateTime lastUpdate;

      and then in the constructor we'll initialize it the first time:

         1: lastUpdate = DateTime.Now;

      and then in the Update method, we need to find the TimeSpan since the last update, set the new lastUpdate time, and then use the time since last update in our calculations:

         1: void Update()
         2: {
         3:     DateTime now = DateTime.Now;
         4:     TimeSpan elapsed = now - lastUpdate;
         5:     lastUpdate = now;
         6:     // do your game loop processing here
         7:     ship.RotationAngle += 100 * elapsed.TotalSeconds;
         8: }

      Now no matter how long between Updates, the ship will rotate at the same speed. Next time we'll look at how we can encapsulate this game loop logic to make it easier to use in other projects.

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

      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 (10) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      Drawing a Sprite

      June 9, 2008 Updated for Silverlight 2 Beta 2

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

         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">
         6:         <Path Data="M0,38 L12,0,24,38,18,32,7,32z" Stroke="#FFFFFFFF" StrokeThickness="2"/>
         7:     </Canvas>
         8: </UserControl>

      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.

      Notice that the default Grid layout panel has been replaced with a Canvas. Layout panels are one of the key concepts of Silverlight development, and you should get a good understanding of the different layout panels that are available so that you can choose the right one for your situation. A Canvas is typically better suited to games, since games usually require precise manual positioning of the visual elements. A Grid is better at organizing elements in rows and columns, and to allow automatic positioning and resizing of elements in the panel.

      So now to add the ship sprite to the game surface. There are two main ways you can put a child object in a layout panel. 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. First we'll add a Canvas to our root Grid in the Page.xaml that will be our game surface which we'll place all of our sprites into. This Canvas will stretch to the size of the Grid panel automatically, so we don't need to specfiy a width or height of the Canvas.  

      Change the page.xaml file to look like this:

         1: <UserControl x:Class="SpaceRocks.Page"
         2:     xmlns="http://schemas.microsoft.com/client/2007" 
         3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
         4:     Width="640" Height="480" xmlns:SpaceRocks="clr-namespace:SpaceRocks">
         5:     <Grid x:Name="LayoutRoot" Background="Black">
         6:         <Canvas x:Name="gameSurface">
         7:             <SpaceRocks:Ship x:Name="ship" Canvas.Left="305" Canvas.Top="220"/>
         8:         </Canvas>
         9:     </Grid>
        10: </UserControl>

      Note the addition of the following namespace declaration:

      xmlns:SpaceRocks="clr-namespace:SpaceRocks"

      This tells Silverlight that if XAML elements are preceded by "SpaceRocks", like our SpaceRocks:Ship element, it should look in the SpaceRocks namespace to find that element, in this case it's a UserControl. It could have just as easily been

      xmlns:my="clr-namespace:SpaceRocks"

      and then our Ship element would have been

      <my:Ship x:Name="ship" Canvas.Left="305" Canvas.Top="220"/>

      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. These are what is called attached properties. Attached properties are properties that are inherited from the parent of the element. Since the parent Canvas is designed to allow for absolute positioning of its elements, it provides a Canvas.Left and Canvas.Top attached property to its children. For comparison, the Grid panel provides a Grid.Row and Grid.Column attached property (among others) to its children.

      The Canvas.Left and Canvas.Top attached properties will become more important as we start animating the ship. We'll start getting into that in the next tutorial.

       

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

      Getting Started with Silverlight 2

      Source code for this tutorial: http://silverlightrocks.com/cs/files/folders/silverlight_2_tutorials_source_code/entry314.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 2008, create a new C# project of type "Silverlight Application". Call it "SpaceRocks". This project will be the basis for the first set of tutorials we'll go through.

      spacerocks

      Click OK, and you'll be prompted for what type of host you want.

      spacerocks_appwizard

      If you're looking to host your Silverlight control in an ASP.NET web form or user control, select the first option. This will generate a test page with the markup you'll need to get your control working in your web site. For this sample, let's keep it simple and select the second option. The reason for a separate project if doing ASP.NET is that Silverlight has its own copies of some of the same assemblies that the web site needs, and since references are specified at the project level, there would be conflicts if it was all in one project.

      Go ahead and compile and run your project. If all goes well, one of two things will happen.

      Outcome 1: You will get prompted with a "Get Silverlight" control. Go ahead and install Silverlight 2 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:

         1: <UserControl x:Class="SpaceRocks.Page"
         2:     xmlns="http://schemas.microsoft.com/client/2007" 
         3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
         4:     Width="640" Height="480">
         5:     <Grid x:Name="LayoutRoot" Background="Black">
         6:  
         7:     </Grid>
         8: </UserControl>

      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 2008 currently has read only 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", change the Width to 640, and the Height to 480. Run it again. Now you should have a black box 640x480 inside the browser.

      blackbox

      Ok so not too impressive, but it's a start. More next time.

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

      My Development Environment for Silverlight 2 Beta 2

      My current development environment for Silverlight 2 Beta 1 is as follows:

      • Microsoft Visual Studio 2008
      • Microsoft Silverlight Tools Beta 2 for Visual Studio 2008 (Contains Tools, Silverlight Runtime and SDK)
      • Microsoft Expression Blend 2.5 June 2008 Preview

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

      One important change for Beta 2. The Silverlight 2 plugin that is installed with the tools is a "Developer Edition", and contains exception details and other symbols to help when debugging Silverlight apps. This is a major step up, since in previous versions it has been very hard to determine the reason for an exception.

      Posted: Mar 11 2008, 21:35 by Bill Reiss | Comments (9) RSS comment feed |
      • Currently 4/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      Back to the Beginning, Sorta

      Instead of updating my previous posts, I'm going to create a series of new posts to get us caught up with the asteroids game in Silverlight 2, then I'll continue from there with finishing the game. This is because things have changed so much, and some of those posts don't really apply to how you'll develop with Silverlight 2. I'm excited to get going on this project and to share some techniques specifically for Silverlight 2.

      Posted: Mar 11 2008, 21:34 by Bill Reiss | Comments (1) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under: