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.

      An improved Silverlight Sprite class

      Source code for this sample: http://www.bluerosegames.com/silverlight-games-101/samples/better-sprites.zip

      When I started working on my next sample which has to do with creating particles, I saw that I had most of what I needed in the Sprite class but I needed to be able to inherit from it to add some functionality. Inheritance is a bit messy in Silverlight for user controls that have XAML, so I started looking into what I could do to make things a bit cleaner but still allow for inheritance. What I ended up with was a pretty clean general purpose sprite class that I was able to move into the BlueRoseGames.Helpers library and then inherited a special case of it to use for the game sprites.

      I’m pretty excited about this because it means that I can add more logic to the Sprite class that can be useful for your own games instead of just having something so specific to this one. It also just “feels” better and not as much of a hack. I’ve had a few people ask me to include a Sprite class in the Helpers library, so here you go. :)

      Anyway I’ll apologize up front for changing things up, but it should be worth it.

      The key to the whole thing is that a UserControl isn’t required to have a XAML file. So I created a Sprite.cs file, inherited from UserControl (so that I can set the Content property with whatever I want the sprite graphics to be), and gave it properties like Position, Velocity, Rotation, and Scale, and a virtual Update method that updated the position based on the velocity, but since it is virtual, it can be overridden in the classes that inherit from Sprite if you want a different behavior.

      So in a new Sprites folder in the BlueRoseGames.Helpers project, here is the Sprite.cs:

      using System.Windows;
      using System.Windows.Controls;
      using System;
      using System.Windows.Shapes;
      using System.Windows.Media;
       
      namespace BlueRoseGames.Helpers.Sprites
      {
          public class Sprite : UserControl
          {
              Point position;
              public Vector Velocity;
              RotateTransform _rotate;
              ScaleTransform _scale;
       
              public Sprite()
              {
                  AddTransforms();
                  Rectangle rect = new Rectangle();
                  rect.Width = 50;
                  rect.Height = 50;
                  rect.Fill = new SolidColorBrush(Colors.Red);
                  SetContent(rect);
              }
       
              public Point Origin
              {
                  get;
                  set;
              }
       
              public Sprite(FrameworkElement content)
              {
                  AddTransforms();
                  SetContent(content);
              }
       
              void AddTransforms()
              {
                  _rotate = new RotateTransform();
                  _scale = new ScaleTransform();
                  TransformGroup transforms = new TransformGroup();
                  transforms.Children.Add(_rotate);
                  transforms.Children.Add(_scale);
                  this.RenderTransform = transforms;
              }
       
              public virtual void SetContent(FrameworkElement content)
              {
                  Content = content;
                  this.Width = content.Width;
                  this.Height = content.Height;
              }
       
              public virtual Point Position
              {
                  set
                  {
                      position = value;
                      this.SetValue(Canvas.LeftProperty, value.X - Origin.X);
                      this.SetValue(Canvas.TopProperty, value.Y - Origin.Y);
                  }
                  get
                  {
                      return position;
                  }
              }
       
              public virtual Vector Scale
              {
                  set
                  {
                      _scale.ScaleX = value.X;
                      _scale.ScaleY = value.Y;
                  }
                  get
                  {
                      return new Vector(_scale.ScaleX, _scale.ScaleY);
                  }
              }
       
              public double RotationAngle
              {
                  get
                  {
                      return _rotate.Angle;
                  }
                  set
                  {
                      _rotate.Angle = value;
                  }
              }
       
       
              public virtual void Update(TimeSpan ElapsedTime)
              {
                  if (Velocity != Vector.Zero) 
                      Position += Velocity * ElapsedTime.TotalSeconds;
              }
       
          }
      }

      This general purpose Sprite class has its transform origin in the upper left corner, and the Position is relative to the upper left corner. For the SpaceRocks game, we want our sprites to be positioned based on the center of the sprite, and also rotate and scale around that point, so I have also created a CenteredSprite class in BlueRoseGames.Helpers which inherits from Sprite and handles this:

      using System.Windows;
       
      namespace BlueRoseGames.Helpers.Sprites
      {
          public class CenteredSprite : Sprite
          {
              public CenteredSprite()
                  : base()
              {
              }
       
              public CenteredSprite(FrameworkElement content)
                  : base(content)
              {
              }
       
              public override void SetContent(FrameworkElement content)
              {
                  base.SetContent(content);
                  Origin = new Point(Width / 2, Height / 2);
                  RenderTransformOrigin = new Point(.5, .5);
              }
          }
      }
      The only real difference is how things are set up in the SetContent method.

      Now for this game, we have a very special need to wrap the objects around the edges, and this used to be in the Sprite class, but now this is in a WrappingSprite class which inherits from CenteredSprite, and this new class lives in the SpaceRocks project:

      using System;
      using System.Windows;
      using BlueRoseGames.Helpers.Sprites;
      using BlueRoseGames.Helpers;
       
      namespace SpaceRocks
      {
          public class WrappingSprite : CenteredSprite
          {
              public WrappingSprite(FrameworkElement content)
                  : base(content)
              {
              }
       
              Point WrapPositionToScreen(Point p)
              {
                  Point result = p;
                  if (result.X > 640) result -= new Vector(640, 0);
                  if (result.X < 0) result += new Vector(640, 0);
                  if (result.Y > 480) result -= new Vector(0, 480);
                  if (result.Y < 0) result += new Vector(0, 480);
                  return result;
              }
       
              public override void Update(TimeSpan ElapsedTime)
              {
                  base.Update(ElapsedTime);
                  Position = WrapPositionToScreen(Position);
              }
          }
      }

      In this case we override the Update method, and then do the base class’ Update, then check to see if we need to wrap the sprite to the other edge of the screen.

      This WrappingSprite is what our game sprites will use or inherit from. A Ship sprite inherits from WrappingSprite and handles using the Ship UserControl in its constructor for the content and adds a Thrust method:

      using System;
      using BlueRoseGames.Helpers;
       
      namespace SpaceRocks
      {
          public class ShipSprite : WrappingSprite
          {
              public ShipSprite()
                  : base(new Ship())
              {
              }
       
              public void Thrust(TimeSpan ElapsedTime)
              {
                  Vector v = MathHelpers.CreateVectorFromAngle(RotationAngle, ElapsedTime.TotalSeconds * 300);
                  Velocity += v;
                  if (Velocity.Length > 500)
                  {
                      Velocity *= (500 / Velocity.Length);
                  }
              }
          }
      }

      and the asteroids are now of type WrappingSprite but are exactly the same besides that.

      Ok so I went through the changes pretty quick, but it’s really just a refactoring, and I encourage you to dig through the source code http://www.bluerosegames.com/silverlight-games-101/samples/better-sprites.zip in more depth if you’re interested in the inner workings of this.

      Posted: Dec 03 2008, 15:00 by Bill Reiss | Comments (0) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:
      Generating Asteroids for the Game

      Source code for this tutorial: http://www.bluerosegames.com/silverlight-games-101/samples/GeneratingAsteroids.zip

      In the original asteroids arcade game, the asteroids themselves were randomly assigned from a predefined set of shapes. I thought it would be more interesting to dynamically generate the outline of each asteroid as we create them.

      The first this we want to do is move the CreateVectorFromAngle and DegreesToRadians methods to another class and make them static and public so that we can use them other places. So in the BlueRoseGames.Helpers project there is a new class called MathHelpers and this is the MathHelpers.cs file:

      using System;
       
      namespace BlueRoseGames.Helpers
      {
          public static class MathHelpers
          {
              public static double DegreesToRadians(double degrees)
              {
                  double radians = ((degrees / 360) * 2 * Math.PI);
                  return radians;
              }
       
              public static Vector CreateVectorFromAngle(double angleInDegrees, double length)
              {
                  double x = Math.Sin(DegreesToRadians(180 - angleInDegrees)) * length;
                  double y = Math.Cos(DegreesToRadians(180 - angleInDegrees)) * length;
                  return new Vector(x, y);
              }
          }
      }

      Change the existing Sprite code to call these static methods instead.

      Now create a new Silverlight User Control in the SpaceRocks project and call it Asteroid.xaml. Add a using statement so that we can use Vector and MathHelpers:

      using BlueRoseGames.Helpers;

      What we’ll do for our asteroids is take a few angles around the circle and using some randomly shuffled radius values, generate a polygon. Then we’ll make these asteroids the content of Sprite objects to get things like positioning, velocity, and wrapping.

      First, change the Asteroid.xaml to have a Canvas for the LayoutRoot and remove the Background property:

      <UserControl x:Class="SpaceRocks.Asteroid"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
          Width="400" Height="300">
          <Canvas x:Name="LayoutRoot">
       
          </Canvas>
      </UserControl>

      The Height and Width don’t matter since we’ll explicitly set those in the constructor anyway. It’s important that we set the Width and Height so that the Sprite object that wraps the Asteroid knows how big to make itself.

      Here is the code behind for the asteroid:

      using System;
      using System.Windows;
      using System.Windows.Controls;
      using System.Windows.Media;
      using System.Windows.Shapes;
      using BlueRoseGames.Helpers;
       
      namespace SpaceRocks
      {
          public partial class Asteroid : UserControl
          {
              static Random rand = new Random();
       
              void shuffle(double[] lengths)
              {
                  for (int i = 0; i < 100; i++)
                  {
                      int i1 = rand.Next(lengths.Length);
                      int i2 = rand.Next(lengths.Length);
                      if (i1 != i2)
                      {
                          double tmp = lengths[i1];
                          lengths[i1] = lengths[i2];
                          lengths[i2] = tmp;
                      }
                  }
              }
       
              public Asteroid(int radius)
              {