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:
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/
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:

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.