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

      Comments

      Comments are closed