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.

      Dr. Dobbs Challenge Silverlight Game Contest

      I received this note about the Dr. Dobbs Challenge, surprised I hadn't heard about it before, or if I did it slipped my mind.

      In addition to weekly and monthly prizes for the creative in-game level submissions, the first big deadline is approaching for best overall conversion. $1,000 will be awarded to the best game that is submitted by April 6 (Monday).

      Developers can download the source code here: http://dobbschallenge2.com/index.php?option=com_remository&Itemid=29
      We welcome creative and innovative games using Silverlight – users can change gameplay mechanics, edit levels, switch graphics, and add code & art to completely modify the look, feel and theme of the game. All details on DobbsChallenge2.com.

      Some nice prizes, and the original game you're modding is not bad either. As long as you start with their code as a base and keep the visual studio logos in it, it sounds like you can do whatever you want with it. There are a few categories, the one button challenge seems pretty interesting. You have until June 12 on most of the prize categories. Good luck!

      Posted: Apr 03 2009, 11:10 by Bill Reiss | Comments (10) RSS comment feed |
      • Currently 1.176471/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      Compile your XNA 2D games to run in Silverlight with SilverSprite

      In December, I released my Dr. Popper game on Xbox Live Community Games (XLCG) which allows you to sell your XNA based games on the Xbox 360. We also started work on Silver Arcade, a hosted game platform for Silverlight games.

      So I started thinking more about how to make a game for both XNA and Silverlight. At first I thought about creating an abstraction layer on top of the two to provide a consistent way of writing games for both. The problem with this is that anyone else who wanted to use this framework would have to learn something new.

      So instead I started to work on something ambitious, and really had no expectations up front that it would actually work. The idea was to create a way to compile your XNA 2D code and content into a Silverlight application. Even better would be if you didn't really have to change much or any code. This actually worked out better than I thought and gave rise to SilverSprite. SilverSprite is available as an open source project on CodePlex and the licensing should allow you to use it in any way you wish.

      I released a new version this weekend that adds some new features. Here is a partial current list:

      • Implements much of SpriteBatch, so many SpriteBatch games should be easy to get working
      • Bitmap fonts are supported
      • Compressed DXT textures are supported to reduce XAP size.
      • Gamepad controls are mapped to keyboard
      • Keyboard and mouse are supported

      One thing I do ask is that if you port your XNA game to Silverlight, that you consider hosting it on Silver Arcade.

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

      Write a Silverlight Game and win $5000 cash!

      Many of you have probably already seen this contest but if you haven't, it's the biggest prize I've ever seen for a Silverlight game contest. Microsoft is sponsoring the Server Quest game contest and you can find more details here:

      http://www.serverquestcontest.com/index.aspx

      Now I have checked with the people running the contest, and they tell me that you can still host your game wherever you want even if you submit it to the contest, so even if your game doesn't win, we'd love to consider it for Silver Arcade, our new site for hosting free web gameswritten exclusively in Silverlight, or win and also submit it to us for the best of both worlds.

      I'd love to submit a game, and I have a good idea for a fairly original game, but I don't know if I'll have the time to actually write it. Good luck to you, you have 50 days left until the deadline.

      Posted: Mar 10 2009, 14:42 by Bill Reiss | Comments (10) RSS comment feed |
      • Currently 5/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      New Silverlight game developers forum on silverlight.net

      It looks like Microsoft is taking Silverlight game development seriously, first we hear about a contest with a $5000 prize at http://www.serverquestcontest.com and now they've launched a new forum just for game development on the official Silverlight developer's site silverlight.net. More about the contest soon, but if you want to check out the new forum, go here:

      http://silverlight.net/forums/52.aspx

      I find it useful to subscribe to the RSS feeds for the silverlight.net forums, if you're interested the feed is here:

      http://silverlight.net/forums/rss.aspx?ForumID=52&Mode=0

      I think it's great to have another place to discuss Silverlight game development, and I hope to see you there.

      Posted: Mar 10 2009, 14:35 by Bill Reiss | Comments (60) RSS comment feed |
      • Currently 2/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      I'm just speechless… What an honor to be the first Silverlight MVP

      Last week, Scott Guthrie announced the addition of a Silverlight expertise for the Microsoft MVP program. Up until now, any MVPs specializing in Silverlight were part of the Client App Dev expertise. Client App Dev includes Windows Forms, WPF, and Silverlight MVPs. With all of the interest in Silverlight and its strategic importance to Microsoft, they felt that it deserved its own category.

      I'm very pleased and humbled to have been named the first official Silverlight MVP. For more about the Silverlight MVP program, please check out Kathy Kam's blog post on the subject:

      http://blogs.msdn.com/kathykam/archive/2009/03/08/announcing-the-new-silverlight-expertise-for-the-mvp-program.aspx

      The MVP program has been very rewarding for me and this is just the latest in a wonderful experience. 

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

      SilverArcade.com Logo contest over, please help us choose

      We have some good news and some bad news. The good news is that we got a lot of great entries in our 99designs.com logo contest. The bad news is that we got a lot of great entries in the logo contest. So we're asking for your help. Please go through the list of logos that we have whittled down to our favorites and post a comment here telling us what you like.

      The contest finalists are here: http://99designs.com/contests/18245

      Thank you for your support and more news soon.

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

      Help us pick a logo for new Silverlight game site SilverArcade.com

      In my last post, I discussed our new game site in the works, and now we're ready to let the cat out of the bag and announce the site name. The site will be SilverArcade.comand we're currently running a contest at 99designs to find a logo. The contest is here:

      http://99designs.com/contests/18245

      Please use the comment space here or there to let us know what you think. This is a site for you and your games, so it makes sense that you should have some input on the logo.

      Posted: Feb 24 2009, 11:22 by Bill Reiss | Comments (22) RSS comment feed |
      • Currently 2.833333/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      Got a Silverlight Game? Want to make some money?

      Over the past year, I've talked with a few of you about my dream of creating a game site exclusively for Silverlight games. I'm pleased to say that I've found a couple of other developers who share my vision and we're actively working on putting the site together. Think Miniclip or Kongregate but for Silverlight games. The site itself is coming along well and will be here before you know it, but what we really need is more great games to host on the site by launch day.

      The basics:

      • We're looking for quality games, we're not going to accept all of them. With any rejection we'll try to give you our suggestions for how the game could be better.
      • Games must be complete, we're not in the business right now of helping you finish a game concept.
      • It's your intellectual property and you keep the rights to it.
      • Get paid based on how many times your game is loaded.
      • Monitor your statistics through a game author dashboard.
      • All games will be free, our revenue model is ad based.

      Special incentives for "launch day" games:

      • Get paid a special bonus up front on top of the pay per load.
      • Do what you want with your game outside of our site, no exclusivity agreements.
      • We'll add links to your site on the game page to help drive traffic to your site.

      Interested? Since you keep control of your intellectual property and can host the game too, or host it somewhere else as well, what do you have to lose? Contact me if you're interested, we look forward to hearing from you. Also if you're an XNA game developer and have an XNA 2D game, we may be able to help you get it ported to Silverlight.

      Posted: Feb 21 2009, 00:18 by Bill Reiss | Comments (91) RSS comment feed |
      • Currently 0/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      Frame Based Sprite Animation in Silverlight

      Code for this sample: http://www.bluerosegames.com/silverlight-games-101/samples/wokka.zip

      There is more than one way to skin a cat. In Silverlight, many time there are several, with many being bad, and a couple being good. Sometimes there is no clear cut winner as to the best way to do something. This is one of those cases, and I'm presenting one out of many ways of potentially doing frame based sprite animation. Some other ways would be to use an ImageBrush or single images. The technique I have chosen is a combination of a "sprite strip" and a clipping region.

      Frame based animation is what they use to develop cartoons, it's been a part of video games since the days of Donkey Kong and earlier. The basic idea is that you flip through a series of images to produce the illusion of motion.

      For this sample, let's create a new Silverlight Application called WokkaAnimation, and add the BlueRoseGames.Helpers project to the solution. This is the same one that we used in the previous SpaceRocks sample, or get it from the sample zip file at the top of this post. Then in the WokkaAnimation project, add a reference to BlueRoseGames.Helpers.

      Modify the Page.xaml to be 640x480 with a Black background:

      <UserControl x:Class="WokkaAnimation.Page"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
          Width="640" Height="480">
          <Canvas x:Name="LayoutRoot" Background="Black">
       
          </Canvas>
      </UserControl>

      Now create a new UserControl called WokkaSpriteContent.xaml. We need an image to animate, so here it is:

      wokka

      Any resemblance to a certain Atari character is strictly coincidental. Add this image to the project as wokka.png

      This image is 500x100, and we'll want the sprite to be 100x100. Change the WokkaSpriteContent.xaml as follows:

      <UserControl x:Class="WokkaAnimation.WokkaSpriteContent"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
          Width="100" Height="100">
          <Canvas x:Name="LayoutRoot">
              <Image x:Name="image" Source="wokka.png"/>
          </Canvas>
      </UserControl>

      Then to create this sprite on our main canvas. Here's the Page.xaml.cs:

      using System.Windows.Controls;
      using BlueRoseGames.Helpers.Sprites;
       
      namespace WokkaAnimation
      {
          public partial class Page : UserControl
          {
              WokkaSpriteContent spriteContent;
              Sprite wokka;
       
              public Page()
              {
                  InitializeComponent();
                  spriteContent = new WokkaSpriteContent();
                  wokka = new Sprite(spriteContent);
                  LayoutRoot.Children.Add(wokka);
              }
          }
      }

      This should look as follows:

      wokkacanvas

      Now we need to hide the images that we don't want to see for the current frame. This is done with the Clip property. Most elements accept a Clip property, in our case we'll clip the Canvas that is the parent of the Image in our WokkaSpriteContent.xaml:

      <UserControl x:Class="WokkaAnimation.WokkaSpriteContent"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
          Width="100" Height="100">
          <Canvas x:Name="LayoutRoot">
              <Canvas.Clip>
                  <RectangleGeometry Rect="0,0,100,100"/>
              </Canvas.Clip>
              <Image x:Name="image" Source="wokka.png"/>
          </Canvas>
      </UserControl>
      There are other options for the Clip property besides a RectangleGeometry, like an Ellipse, Path, or Line.

      Now you should only see one frame of the sprite.

      For the animation, we can reposition the Image so that the part we want is showing through the Clip region. The formula for the value of the Canvas.Left property is:

      left = - (frame index * frame width)

      Here's WokkaSpriteContent.xaml.cs after adding an Update method to do this:

      using System;
      using System.Windows.Controls;
       
      namespace WokkaAnimation
      {
          public partial class WokkaSpriteContent : UserControl
          {
              TimeSpan animationDelay = TimeSpan.FromSeconds(.05);
              TimeSpan timeSinceLast = TimeSpan.Zero;
              int currentFrame = 0;
              int delta = 1;
              public WokkaSpriteContent()
              {
                  InitializeComponent();
              }
       
              public void Update(TimeSpan elapsed)
              {
                  timeSinceLast += elapsed;
                  if (timeSinceLast > animationDelay)
                  {
                      timeSinceLast -= animationDelay;
                      currentFrame+=delta;
                      if (currentFrame == 4) delta = -1;
                      if (currentFrame == 0) delta = 1;
                      image.SetValue(Canvas.LeftProperty, currentFrame * -100d);
                  }
              }
          }
      }

      In this case, we'll cycle from frame 0 to frame 4 then go back to 0, and repeat. The changing of frame will happen every .05 seconds. Now all we have to do is call the WokkaSpriteContent's from a game loop. Here is the finished Page.xaml.cs:

      using System.Windows.Controls;
      using BlueRoseGames.Helpers.Sprites;
      using BlueRoseGames.Helpers.Timers;
       
      namespace WokkaAnimation
      {
          public partial class Page : UserControl
          {
              WokkaSpriteContent spriteContent;
              Sprite wokka;
              CompositionTargetGameLoop gameLoop;
       
              public Page()
              {
                  InitializeComponent();
                  spriteContent = new WokkaSpriteContent();
                  wokka = new Sprite(spriteContent);
                  LayoutRoot.Children.Add(wokka);
                  gameLoop = new CompositionTargetGameLoop();
                  gameLoop.Update += new GameLoop.UpdateHandler(gameLoop_Update);
                  gameLoop.Start();
              }
       
              void gameLoop_Update(object sender, System.TimeSpan elapsed)
              {
                  spriteContent.Update(elapsed);
              }
          }
      }
      Posted: Jan 10 2009, 05:57 by Bill Reiss | Comments (76) RSS comment feed |
      • Currently 1/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      Dr. Popper now on Xbox Live Community Games

      This post isn’t strictly Silverlight game related, but since you may have played the Silverlight version, I thought you might be interested.

      I’m thrilled to announce that Dr. Popper has passed peer review and is now available through Xbox Live Community Games for only 200 points. It has all of the game modes from the Silverlight version plus improved graphics, background music, and a free trial mode. Here is the link to the game on xbox.com:

      http://marketplace.xbox.com/en-US/games/media/66acd000-77fe-1000-9115-d80258550147/

      and a screenshot of Extreme mode:

      screenshot2

      I’m also pleased to announce that it’s currently #2 in popularity for puzzle games in Xbox Live Community Games and #8 overall. So give it a try, tell your friends, and thanks for your support!

      http://marketplace.xbox.com/en-US/games/media/66acd000-77fe-1000-9115-d80258550147/

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

      A simple Silverlight game particle system

      Source code: http://www.bluerosegames.com/silverlight-games-101/samples/particles.zip

      Particles are a key part of game programming. When it comes to explosions, smoke effects, and many other types of effects, it is often easier and gives a better effect to compose the full effect out of several smaller particles. These particles can vary in velocity, position, color, and opacity, among other properties, based on the effect desired. In some games, the particles need to behave according to the game physics, such as gravity. Particles typically have a short life span and then disappear. Since there can be a large amount of particles being created and destroyed on a regular basis, a particle engine is typically employed to manage them.

      The first particles we will add to the game is the exhaust trail coming out of the back of the ship when the thrust key is pressed. To do this, we will create a new particle in each frame, and calculate a velocity opposite to the current direction of the ship. The particles will fade from full opacity to partial opacity over the life of the particle, and then disappear at the end of its life span.

      Since particles are such an important part of game development, I have added a ParticleEngine class to the BlueRoseGames.Helpers library in the BlueRoseGames.Helpers.Sprites namespace to make managing particles simple. Along with this is a Particle class which inherits from CenteredSprite and adds some logic specific to particles, like a start opacity, end opacity, start scale, end scale, and life span. The Update method is overridden to call the base update method, check to see if its life span is over, and if still alive, set the opacity and scale based on the time the particle has been alive. Once you create a new particle, simply call ParticleEngine.AddParticle, passing in the Particle object.

      First let’s look at the Particle class. In the Update method, we keep track of how long the particle has been alive, and if it is past its life span, we set its Dead property to true, otherwise we set the current scale and opacity values based on how long it’s been alive and the start and end values specified. You could easily override this Update method if you inherit from Particle and do your own thing, like use Storyboard animations, or frame based image animations, or whatever. InterpolateDouble is just a helper method that does a linear interpolation to find the current value from the start and end values and the percentage of time that has elapsed.

      using System;
      using System.Windows;
       
      namespace BlueRoseGames.Helpers.Sprites
      {
          public class Particle : CenteredSprite
          {
              protected TimeSpan lifeSoFar = TimeSpan.Zero;
       
              public Particle()
                  : base()
              {
                  FromScale = ToScale = 1;
                  FromOpacity = ToOpacity = 1;
                  Dead = false;
              }
       
              public Particle(FrameworkElement content) : base(content)
              {
                  FromScale = ToScale = 1;
                  FromOpacity = ToOpacity = 1;
                  Dead = false;
              }
              
              public TimeSpan TimeToLive { get; set; }
       
              public double FromOpacity { get; set; }
              public double ToOpacity { get; set; }
       
              public double FromScale { get; set; }
              public double ToScale { get; set; }
       
              public bool Dead { get; set; }
       
              double InterpolateDouble(double start, double end, double pct)
              {
                  return start + (end - start) * pct;
              }
       
              public override void Update(TimeSpan ElapsedTime)
              {
                  base.Update(ElapsedTime);
                  lifeSoFar += ElapsedTime;
                  if (lifeSoFar > TimeToLive)
                  {
                      Dead = true;
                  }
                  else
                  {
                      double pct = lifeSoFar.TotalSeconds / TimeToLive.TotalSeconds;
                      Opacity = InterpolateDouble(FromOpacity, ToOpacity, pct);
                      double scale = InterpolateDouble(FromScale, ToScale, pct);
                      Scale = new Vector(scale, scale);
                  }
              }
          }
      }

      In the case of the exhaust trail, we will use ellipses (actually circles), but your particle could be a canvas with many children, an image, or any other FrameworkElement because Particle inherits from CenteredSprite which inherits from Sprite, and Sprite provides this ability.

      The ParticleEngine is pretty simple. It stores a collection of Particle objects, and in its Update method, calls the Update of everything in its list, and checks to see if it’s time to remove them. It also handles adding the particle to the host canvas when the particle is added, and removing it when it’s dead.

      using System;
      using System.Windows.Controls;
      using System.Collections.Generic;
       
      namespace BlueRoseGames.Helpers.Sprites
      {
          public class ParticleEngine
          {
              Canvas _canvas;
              List<Particle> particles = new List<Particle>();
              public ParticleEngine(Canvas canvas)
              {
                  _canvas = canvas;
              }
       
              public void AddParticle(Particle p)
              {
                  particles.Add(p);
                  _canvas.Children.Add(p);
              }
       
              public void Update(TimeSpan ElapsedTime)
              {
                  for (int i = particles.Count - 1; i >= 0; i--)
                  {
                      Particle p = particles[i];
                      p.Update(ElapsedTime);
                      if (p.Dead)
                      {
                          particles.Remove(p);
                          _canvas.Children.Remove(p);
                      }
                  }
              }
          }
      }

      So now that we have a general purpose particle system, let’s use it. First of all, to encapsulate the slightly complex initialization logic of the exhaust particles, let’s create an ExhaustParticle class in the SpaceRocks project that inherits from Particle:

      using System;
      using System.Windows.Media;
      using System.Windows.Shapes;
      using BlueRoseGames.Helpers.Sprites;
      using BlueRoseGames.Helpers;
       
      namespace SpaceRocks
      {
          public class ExhaustParticle : Particle
          {
              static Random rand = new Random();
       
              public ExhaustParticle(Sprite ship) : base()
              {
                  Ellipse ellipse = new Ellipse();
                  ellipse.Fill = new SolidColorBrush(Colors.White);
                  ellipse.Width=4;
                  ellipse.Height=4;
                  SetContent(ellipse);
                  FromOpacity = 1;
                  ToOpacity = .3;
                  TimeToLive = TimeSpan.FromSeconds(.2);
                  Vector v = -MathHelpers.CreateVectorFromAngle(ship.RotationAngle, 1);
                  Velocity = v * rand.Next(150, 250);
                  Vector offset = MathHelpers.CreateVectorFromAngle(rand.Next(0, 360), rand.Next(10, 50) / 10d);
                  Position = ship.Position + v * 19 + offset;
              }
          }
      }

      Basically we’re creating a white ellipse as the content of the particle, giving it a start opacity of 1 and end opacity of 3, and specifying that it should live for .2 seconds. Then we calculate the velocity based on the direction the ship is facing and the starting position based on the position of the ship. The offset is calculated to give the particles a slight randomness on position so they don’t all shoot out from the same exact point in a straight line. All of the rest of the functionality is handled by the Particle base class.

      Now in the Page class, let’s declare the particle engine:

      ParticleEngine particleEngine;

      And then in the Page’s constructor, we’ll create it, passing in the canvas we want the particles to be displayed on:

      particleEngine = new ParticleEngine(gameSurface);

      Now simply in the gameLoop_Update method in the Page class, any type we add thrust to the ship, also create an exhaust particle and add it to the particle engine:

      void gameLoop_Update(object sender, TimeSpan elapsed)
      {
          // do your game loop processing here
          if (keyHandler.IsKeyPressed(Key.A) || keyHandler.IsKeyPressed(Key.Left))
          {
              ship.RotationAngle -= rotationSpeed * elapsed.TotalSeconds;
          }
          else if (keyHandler.IsKeyPressed(Key.D) || keyHandler.IsKeyPressed(Key.Right))
          {
              ship.RotationAngle += rotationSpeed * elapsed.TotalSeconds;
          }
          if (keyHandler.IsKeyPressed(Key.W))
          {
              ship.Thrust(elapsed);
              particleEngine.AddParticle(new ExhaustParticle(ship));
          }
          ship.Update(elapsed);
          foreach (Sprite asteroid in asteroids)
          {
              asteroid.Update(elapsed);
          }
          particleEngine.Update(elapsed);
      }

      Notice also that we have added a call to the particle engine’s Update method. This needs to be called in our game loop so that all of the particles update properly and are removed when their life span is complete.

      So 4 lines of code plus a few more to initialize the particle, and we have a pretty nice effect. For explosions, you would do something similar, but make the velocity a random direction from a center point, and we’ll see that in a future sample.

      And here is the result:

      spacerocks_exhaust

      Posted: Dec 03 2008, 23:33 by Bill Reiss | Comments (22) RSS comment feed |
      • Currently 5/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      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 (12) RSS comment feed |
      • Currently 4.666667/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)
              {
                  InitializeComponent();
                  this.Width = radius * 2;
                  this.Height = radius * 2;
                  double[] lengths = { 1, 1, 1, .97, .97, .94, .94, .91, .91, .88, .85, .82, .82, .79, .76, .73, .61, .51 };
                  shuffle(lengths);
                  Polygon poly = new Polygon();
                  poly.Stroke = new SolidColorBrush(Colors.White);
                  poly.StrokeThickness = 1.5;
                  for (int i = 0; i < 18; i++)
                  {
                      float degrees = i * 20;
                      Vector v = MathHelpers.CreateVectorFromAngle(degrees, radius * lengths[i]);
                      poly.Points.Add(new Point(v.X + radius, v.Y + radius));
                  }
                  LayoutRoot.Children.Add(poly);
              }
          }
      }

      The shuffle() method will take an array and shuffle the elements of the array randomly, like in a deck of cards. In our case, we will be shuffling distances from the center of the object.

      Notice that I have added a radius parameter to the Asteroid constructor. This will allow us to easily create asteroids of varying sizes.

      The first thing to do is set the height and width of the asteroid based on the radius, then the lengths array is a series of percentages stored as double values. The lengths array determines for each of 18 points along the outside of the asteroid (one for every 20 degrees) how far that point will be from the center, with a value of 1 representing a distance equal to the radius, and a value of .5 would be half of the radius.

      This array is shuffled so that the percentage values are mixed up and each asteroid will look differently. I use this technique instead of just randomly generating the distance from the center values to avoid asteroids with values that fluctuate too much or ones where you get a bunch of small values, etc. This seems to work pretty well.

      Next we create our polygon, and going in 20 degree intervals, calculate the vector from the center for each point and add the point to the points collection for the polygon. Then we add the polygon to the root canvas for the Asteroid control.

      Then all that's left is to randomly generate a starting position and velocity, and to set the Width and Height properties of the user control (inherited from Sprite).

      Ok so now to use this class. In the Page class, add a field to hold a list of Asteroids:

      List<Sprite> asteroids = new List<Sprite>();

      and then, at the end of the Page_Loaded method, add the following:

      for (int i = 0; i < 4; i++)
      {
          Sprite a = new Sprite(new Asteroid(40));
          asteroids.Add(a);
          gameSurface.Children.Add(a);
      }

      Note that the asteroids are added both to our list of Asteroids (so that they can be easily accessed later in the Update and other methods) and to the list of Children for the game surface. If you don't add your user control to the list of children for the game surface, they will not be displayed.

      Then to move the asteroids, in the gameLoop_Update method, add the following:

      foreach (Sprite asteroid in asteroids)
      {
          asteroid.Update(elapsed);
      }

      so if all went well, if you run the program, you should see 4 asteroids floating around.

      asteroids

      Posted: Nov 23 2008, 22:25 by Bill Reiss | Comments (33) RSS comment feed |
      • Currently 4/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under:

      Adding Thrust

      Full source for this tutorial: http://www.bluerosegames.com/silverlight-games-101/samples/AddingThrust.zip

      A famous April Fools Day network RFC stated:

      With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead.

      Fortunately, we're not dealing with pigs, we're dealing with space ships, although a DreamBuildPlay contest warmup entry replaced space ships with space cows http://udderassault.whatsupnow.com/ so I guess you could make this game with space pigs, but I digress.

      So what do we need to do to make the ship move around the screen?

      • We need to store the current velocity and current position of the ship somewhere
      • When the "up" key is pressed, we need to change the velocity based on the direction the ship is currently pointing, and compare this new value against the maximum velocity of the ship and adjust it appropriately if it exceeds the max velocity
      • Based on the velocity, we need to reposition the ship in the game loop
      • If the ship hits an edge, we need to "wrap" to the other end of the screen by repositioning the ship

      During my previous work with game frameworks, the data type that was used more than any other was the Vector. Unfortunately, the Vector value type is available in WPF, but is omitted in Silverlight 2. Because I feel it is important to have access to Vector in game programming, I have implemented my own version of the Vector struct in BlueRoseGames.Helpers. It follows the interface of the WPF version, so if it becomes available, it should be easy to switch.

      Vectors are made up of a direction and a magnitude, and in games programming, are most commonly represented as an X and Y value. A vector with a magnitude of 3 in the X direction and 1 in the Y direction can be written as (3,1). We will be using Vectors to store the velocities of our sprites in this game.

      Repositioning a Sprite

      It may seem strange at first that the User Interface elements in Silverlight don't have a Left or Top property. The explanation that I have heard for this is that Canvases (and other Xaml graphics elements) are not always absolutely positioned, many times they are in a "flow" layout, where there is no absolute positioning, it is all relative to the container and other elements within that container. For this reason, the absolute positioning properties are not built in to the object, and must be accessed indirectly through the SetValue and GetValue methods.

      So to set the Left position of an element, you do something like the following:

      this.SetValue(Canvas.LeftProperty, 100d);

      Since this is a bit verbose, let's encapsulate this logic in a Property. Position and Velocity are going to be needed for each sprite in our game, so it makes sense to create a class which our other sprites can inherit from so that we don't have to duplicate this logic. So let's create a user control called Sprite in our SpaceRocks project. Here is the XAML for the Sprite user control:

      <UserControl x:Class="SpaceRocks.Sprite"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
          Width="50" Height="50" RenderTransformOrigin=".5,.5">
          <UserControl.RenderTransform>
              <RotateTransform x:Name="rotate"/>
          </UserControl.RenderTransform>
          <Grid x:Name="LayoutRoot" Background="Red">
       
          </Grid>
      </UserControl>

      And this is the code behind:

      using System.Windows;
      using System.Windows.Controls;
      using BlueRoseGames.Helpers;
       
      namespace SpaceRocks
      {
          public partial class Sprite : UserControl
          {
              Point position;
              public Vector Velocity;
       
              public Sprite()
              {
                  InitializeComponent();
              }
       
              public Sprite(FrameworkElement content)
              {
                  InitializeComponent();
                  this.Content = content;
                  this.Width = content.Width;
                  this.Height = content.Height;
              }
       
              public Point Position
              {
                  set
                  {
                      position = value;
                      this.SetValue(Canvas.LeftProperty, value.X - Width / 2);
                      this.SetValue(Canvas.TopProperty, value.Y - Height / 2);
                  }
                  get
                  {
                      return position;
                  }
              }
       
              public double RotationAngle
              {
                  get
                  {
                      return rotate.Angle;
                  }
                  set
                  {
                      rotate.Angle = value;
                  }
              }
          }
      }

      Notice that when setting the position, we subtract half the length and half the width. This is so that Position actually corresponds to the center of the object. This is a matter of preference and also depends on the type of game. In this case, I felt it made sense to center the objects around their position values. If the X and Y values were set without this offset, the position of the sprite would correspond to the top left corner.

      Now instead of declaring the ship sprite in the Page.xaml, let’s declare the Ship as type Sprite and create it in the Page constructor. First in Page.xaml.cs, declare the ship sprite:

      Sprite ship;

      Then make the Page constructor look like this:

      public Page()
      {
          InitializeComponent();
          ship = new Sprite();
          ship.Position = new Point(320, 240);
          gameSurface.Children.Add(ship);
          this.GotFocus += new RoutedEventHandler(Page_GotFocus);
          this.LostFocus += new RoutedEventHandler(Page_LostFocus);
          this.MouseLeftButtonDown += new MouseButtonEventHandler(Page_MouseLeftButtonDown);
          gameLoop = new CompositionTargetGameLoop();
          gameLoop.Update += new GameLoop.UpdateHandler(gameLoop_Update);
          keyHandler = new KeyHandler(this);
      }

      So basically we’re creating the sprite for the ship, setting its position, and adding it to the gameSurface canvas. Everything else can stay the same. Now if you run the game, it will look like this:

      redsprite

      The sprite is a red block since that’s what the XAML for the Sprite control has by default. Notice, however, that there’s another constructor on the Sprite control which takes a FrameworkElement:

      public Sprite(FrameworkElement content)
      {
          InitializeComponent();
          this.Content = content;
          this.Width = content.Width;
          this.Height = content.Height;
      }

      When you use this one, whatever is specified for the FrameworkElement is used as the content for the sprite. So if we change the ship creation in the Page constructor to this:

      ship = new Sprite(new Ship());

      Then things will look and behave like they did before. The difference is that now we have a Sprite control that we can reuse for other sprites in the game and it can have our common sprite logic in it.

      Now to add code to change the position of the ship. First, if we're thrusting, we need to modify the velocity of the ship based on the ship's current rotation. Let's add a method to the Sprite class called Thrust, and a couple of helper methods:

      private double DegreesToRadians(double degrees)
      {
          double radians = ((degrees / 360) * 2 * Math.PI);
          return radians;
      }
       
      public 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);
      }
       
      public void Thrust(TimeSpan ElapsedTime)
      {
          Vector v = CreateVectorFromAngle(RotationAngle, ElapsedTime.TotalSeconds * 300);
          Velocity += v;
          if (Velocity.Length > 500)
          {
              Velocity *= (500 / Velocity.Length); 
          }
      }

      So what does this do? It converts the current rotation and a magnitude to a Vector. This Vector is in the direction that the ship is currently facing. Then we add that to the current velocity. Then, if the magnitude (Length) of the Velocity is greater than a maximum Velocity of 500, we scale the Velocity vector to have a magnitude of 500.

      Now, we need code to call the Thrust method if a certain key is pressed. We'll use the "W" key. So in the gameLoop_Update method in the Page class, add the following:

      if (keyHandler.IsKeyPressed(Key.W))
      {
          ship.Thrust(elapsed);
      }

      Now all we need to do is to update the position based on the velocity for the sprite in the game loop. We also need to see if the sprite has gone off the screen, and if so, we need to wrap it to the other edge of the screen. Add the following to the Sprite class:

      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 void Update(TimeSpan ElapsedTime)
      {
          Position = WrapPositionToScreen(Position + Velocity * ElapsedTime.TotalSeconds);
      }

      And then in the gameLoop_Update method of the Page class, call this update method for the ship object:

      ship.Update(elapsed);

      and we're done for now. Run the program again and you should be able to maneuver the ship.

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

      Silverlight CompositionTarget.Rendering Game Loop

      Sample code: http://www.bluerosegames.com/silverlight-games-101/samples/CompositionTargetGameLoop.zip

      So far we have a game loop implementation based on the DispatcherTimer, and another based on and empty Storyboard. Silverlight 2 RTW introduces another option, the CompositionTarget.Rendering event. Since it’s been a while, first let’s take another look at the GameLoop base class in BlueRoseGames.Helpers.Timers:

      using System;
       
      namespace BlueRoseGames.Helpers.Timers
      {
          public abstract class GameLoop
          {
              protected DateTime lastTick;
              public delegate void UpdateHandler(object sender, TimeSpan elapsed);
              public event UpdateHandler Update;
       
              public void Tick()
              {
                  DateTime now = DateTime.Now;
                  TimeSpan elapsed = now - lastTick;
                  lastTick = now;
                  if (Update != null) Update(this, elapsed);
              }
       
              public virtual void Start()
              {
                  lastTick = DateTime.Now;
              }
       
              public virtual void Stop()
              {
              }
          }
      }

      This base class has the logic and data to keep track of the elapsed time between updates of the game loop and calls the Update event. The inherited classes only need to implement their specific start and stop logic, and they need to call the GameLoop’s Tick method when events fire.

      The CompositionTarget.Rendering event fires just before each frame is rendered, so it’s a good way to create a game loop since that’s when we really need to update everything. Here is our new CompositionTargetGameLoop based on this event:

      using System;
      using System.Windows.Media;
       
      namespace BlueRoseGames.Helpers.Timers
      {
          public class CompositionTargetGameLoop : GameLoop
          {
              public CompositionTargetGameLoop()
              {
              }
       
              void CompositionTarget_Rendering(object sender, EventArgs e)
              {
                  base.Tick();            
              }
       
              public override void Start()
              {
                  CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
                  base.Start();
              }
       
              public override void Stop()
              {
                  CompositionTarget.Rendering -= new EventHandler(CompositionTarget_Rendering);
                  base.Stop();
              }
          }
      }

      So in the Start event, we attach an event handler to the Rendering event, and in the Stop event we remove the event handler. Then in the Rendering event, we simply call the GameLoop’s Tick method which fires the Update event.

      Then to use this timer in the Space Rocks game sample. Here’s the new Page.xaml.cs after the changes:

      using System;
      using System.Windows;
      using System.Windows.Controls;
      using System.Windows.Input;
      using BlueRoseGames.Helpers.Timers;
      using BlueRoseGames.Helpers.Keyboard;
       
      namespace SpaceRocks
      {
          public partial class Page : UserControl
          {
              CompositionTargetGameLoop 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 CompositionTargetGameLoop();
                  gameLoop.Update += new GameLoop.UpdateHandler(gameLoop_Update);
                  keyHandler = new KeyHandler(this);
              }
       
              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();
              }
       
              void gameLoop_Update(object sender, TimeSpan elapsed)
              {
                  // do your game loop processing here
                  if (keyHandler.IsKeyPressed(Key.A) || keyHandler.IsKeyPressed(Key.Left))
                  {
                      ship.RotationAngle -= rotationSpeed * elapsed.TotalSeconds;
                  }
                  else if (keyHandler.IsKeyPressed(Key.D) || keyHandler.IsKeyPressed(Key.Right))
                  {
                      ship.RotationAngle += rotationSpeed * elapsed.TotalSeconds;
                  }
              }
       
          }
      }
       

      It’s now included in BlueRoseGames.Helpers.Timers so that you can use it in your own projects.

      Posted: Nov 23 2008, 08:19 by Bill Reiss | Comments (21) RSS comment feed |
      • Currently 5/5 Stars.
      • 1
      • 2
      • 3
      • 4
      • 5
      Filed under: