Mobile Zone is brought to you in partnership with:

My name is Toni Petrina and I am a software developer and an occasional speaker. Although I primarily develop on the Microsoft stack, I like to learn new technologies. My hobbyist projects range from game development, regardless of the technology, to ALM. I spend most of my time with my girlfriend and someday I will learn how to play the guitar properly. Toni is a DZone MVB and is not an employee of DZone and has posted 49 posts at DZone. You can read more from them at their website. View Full User Profile

Creating a Simple Particle Effect With XNA For WP7

07.24.2012
| 1914 views |
  • submit to reddit

Creating a particle effect is just too much fun without sharing it. The objective was to create a simple spark-like particle effect. We will render each spark as a small line that gets transparent with time. Some basic physics will be involved, but I will keep it simple. The final result is seen on the image below. You can download the source code here: wp7.particles (SkyDrive, 26 kB). Please note that the default orientation for this sample is landscape.



First we must decide how to represent the rendering data and then initialize our basic effect. Since we are rendering sparks as lines and we only need position and color, we can use a built in class named VertexPositionColor. Each line consists of two points and we will render all particles in one try. Put the following code in the Initialize method:

// the declaration describes input vertex data
vertexDeclaration = new VertexDeclaration(new VertexElement[]
    {
        new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
        new VertexElement(12, VertexElementFormat.Color, VertexElementUsage.Color, 0)
    }
);
 
basicEffect = new BasicEffect(GraphicsDevice);
basicEffect.VertexColorEnabled = true;
 
basicEffect.World = Matrix.Identity;
// make sure that we render inside (800x480) rectangle
// where (0,0) is the upper left corner, and (800, 480) is the lower right corner
basicEffect.View = Matrix.CreateScale(1, -1, 1) * Matrix.CreateTranslation(-GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2, 0);
// entire area is rendered flat
basicEffect.Projection = Matrix.CreateOrthographic(GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, 0, 10);

The code is straightforward for those who have dealt with 3D graphics before. For those who are new to the 3D graphics, we need to setup matrices that describe how data is transformed from 3D coordinates to the screen coordinates. The last three lines in the code snippet above ensure that we can use the following drawing rectangle: [0, 800] x [0, 480] similar to the sprite batch.

Next stop is defining a particle. Particle needs to know where it is in the world (position vector), where it is headed (direction vector), how long it will live (remaining lifetime) and what was the original life span when it was created. The code for the class is given in the snippet below.

class Particle
{
    public Vector3 pos, dir;
    public double life;
    public double span;
}

Our update method will do most of the hard work. It will consist of three parts:

  1. Update each particle
  2. Remove dead particles (whose lifetime is less or equal to zero or if they are off screen)
  3. If there are less than a fixed number of particles, create new ones randomly

First update life and position for the current particle. Also, apply gravitational force on the particle, this will make sparks fall realistically. The following code goes into the Update method:

foreach (var particle in _particles)
{
    particle.life -= gameTime.ElapsedGameTime.TotalMilliseconds;
    // very precise constant for gravity
    particle.dir += new Vector3(0,
                                9.81f * (float)gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f,
                                0);
    particle.pos += particle.dir;
}

Next step is to remove dead particles. If only we have RemoveAll function…

_particles = _particles.Where(item => item.life >= 0 && item.pos.Y <= graphics.GraphicsDevice.Viewport.Height).ToList();

Finally, randomly create particles if there are to little particles on screen.

if (_particles.Count() > 1000)
  return;
 
for (int i = 0; i < _random.Next(20, 100); ++i)
{
    // live from 2 seconds up to 4
    var lifetime = _random.Next(2000, 4000);
    _particles.Add(new Particle()
    {
        life = lifetime,
        span = lifetime,
        // initial position at the bottom center
        pos = new Vector3(graphics.GraphicsDevice.Viewport.Width / 2,
                          graphics.GraphicsDevice.Viewport.Height,
                          0),
        // they go up and randomly left-right, also, ignore Z axis
        dir = new Vector3((float)_random.NextDouble() * 5 - 2.5f,
                          -12 - (float)_random.NextDouble() * 4,
                          0)
    });
}

 Sheesh, that was a lot of code. Now onto the rendering part. Render each particle as a small line, starting from its position and back along the direction vector. The following code goes into the Draw method:

foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
{
    pass.Apply();
    var data = new List<VertexPositionColor>(_particles.Count * 2);
    foreach (var particle in _particles)
    {
        var color = Color.FromNonPremultiplied(224, 53, 27, (int)(255f * (particle.life / particle.span)));
        data.Add(new VertexPositionColor(particle.pos, color));
        data.Add(new VertexPositionColor(particle.pos - particle.dir, color));
    }
    GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, data.ToArray(), 0, _particles.Count);
}
Published at DZone with permission of Toni Petrina, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)