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.

      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:

      Comments

      Comments are closed