Creating Particle Effects in Silverlight

Governor Technology is a London-based software consultancy, specialising in ASP.NET & Silverlight development. Our clients include Microsoft, Thomson Reuters & Citigroup. We've been a Microsoft Certified Partner since 2004 and a Silverlight Partner since 2008.

Introduction

The term particle effects describes a computer animation technique where many small particles are combined to create realistic special effects that would otherwise be difficult to achieve using standard animation techniques. Typical examples of the visual effects that are achieved with this approach include: fire, water, smoke, fog, snow and sparkles.

In this post we look at a simple implementation of a particle system in Silverlight and use it to create a realistic smoke effect. The finished animation is shown below:

Get Microsoft Silverlight

Creating the Particle

The first step is to create a single particle which will form the basis of the smoke effect. This will be a soft grey ellipse which oscillates back and forth horizontally. When many of these particles are combined and animated together they will create the smoke effect.

To start, create a new Silverlight application in Microsoft Visual Studio 2008. Accept the default option to create a web site to host the application.

Create a user control to represent the particle and call this SmokeParticle.xaml. Set the user control to a canvas layout, change the size to 60x40 and make the background transparent.

<UserControl x:Class="ParticleEffectsDemo.SmokeParticle"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   Width="60" Height="40" Background="#FF000000">
    <Canvas x:Name="LayoutRoot" Background="Transparent">
    </Canvas>
</UserControl>

Switch to expression blend and draw an ellipse 40 pixels by 40 pixels. Fill it with a radial gradient brush from #FFA0A0A0 in the centre to #00FFFFFF at the edge. Name this ellipse "Particle".

The smoke particle in Expression Blend

Fig 1: The smoke particle in Expression Blend

We are now going to add the oscillating animation to the smoke particle. Create a new storyboard called "drift". Create keyframes at 0s, 0.5s and 1s, then add an X transform at each keyframe so that the particle goes from 0px to 10px and finally 20px. Select the storyboard and set it to auto reverse and repeat forever. If you play this animation the particle moves from left to right and back again at uniform speed.

The next step is to add some easing to vary the speed at which the particle moves. To do this we edit the keyframe splines so that the middle keyframe has x1=0.5 and the final keyframe has x2=0.5. If you play the animation now you will see that the particle speeds up nearer the middle of the animation.

Example of easing on the final keyframe Fig 2: Example of easing on the final keyframe

The Particle Generator

Having created a user control for the particle, we now need an emitter to generate multiple particles on the display surface. Rather than generate endless new instances of the particle control and leave it to the garbage collector to clean up, it is good practice to maintain a pool of particles and reuse them when they expire. To help us do this we create a class called ParticleGenerator, which maintains a pool of particles in a queue and has methods to get a particle from the queue and return a particle to the queue when it is no longer needed. The code for the class is below. As you can see this is a generic class, which allows us to maintain pools of any type of particle.

public class ParticleGenerator where T : new()
    {
        const int DEFAULT_SIZE = 10;
        Queue particles;

        public ParticleGenerator()
        {
            particles = new Queue();
            CreateParticles(DEFAULT_SIZE);
        }

        private void CreateParticles(int n)
        {
            for (int i = 0; i < n; i++)
                particles.Enqueue(new T());
        }

        public T GetParticle()
        {
            if (particles.Count == 0)
            {
                CreateParticles(DEFAULT_SIZE);
            }

            return particles.Dequeue();
        }

        public void ReturnParticle(T p)
        {
            particles.Enqueue(p);
        }
    }

The Animation Loop

The main animation look is coded in Page.xaml.cs. We use a DispatcherTimer to control the animation loop and call function that creates new particles, moves each particle through its lifecycle and expires them at the end. To start with we define some variables that control the smoke effect:

private const double fadeSpeed = 0.015D; private const double floatUpSpeed = 2D; private const int driftDelay = 1000; private DispatcherTimer timerLoop; private ParticleGenerator particles = new ParticleGenerator();

fadeSpeed: controls how quickly particles fade from view and are retired from the canvas. The higher this value the shorter the smoke trail. floatUpSpeed: controls the speed at which particles float up the screen. driftDelay: controls the random delay of the drift animation of when particles are created, which prevents the drift effect being too uniform.

Notice that we also instantiate an instance of the ParticleGenerator class here, which will be used in the creation of new particles during the animation loop.

In the page constructor, we initialise the timerLoop to start calling the animation loop event handler timerLoop_Tick at regular intevals of fifty milliseconds:

timerLoop = new DispatcherTimer();
timerLoop.Interval = TimeSpan.FromMilliseconds(50);
timerLoop.Tick += new EventHandler(timerLoop_Tick);
timerLoop.Start();

The next step is to write the function that will create new particles on the display surface. This function uses the ParticleGenerator class to get new particles from the pool rather than creating them directly. private void CreateParticle(Point pt) { // Create a new particle SmokeParticle p; ScaleTransform st; p = particles.GetParticle();

// Scale it to the correct size
st = new ScaleTransform();
st.CenterX = 20;
st.CenterY = 20;
st.ScaleX = 0.1;
st.ScaleY = 0.1;
p.RenderTransform = st;

// Set opacity to visible
p.Opacity = 1;

// Set the particle start position with a slight random offset
p.SetValue(Canvas.LeftProperty, new Random().Next(5) + pt.X);
p.SetValue(Canvas.TopProperty, pt.Y);

// Start the drift animation on the particle
p.Drift.BeginTime = TimeSpan.FromMilliseconds(new Random().Next(driftDelay));
p.Drift.Begin();

// Add to the canvas
LayoutRoot.Children.Add(p);

}

This function first gets a particle from the ParticleGenerator. Then scales it down to the correct size by adding a ScaleTransform to the control. The opacity is reset to one because expired particles that have been reused will have an opacity of zero. The particle is then positioned with a random offset to introduce a degree of variation to the smoke, and the drift animation is started with a random delay. Finally, the new particle is placed on the canvas to be displayed.

The final step is to animate all the particles on the display surface each time around the animation loop so that they drift upwards together to create the realistic smoke effect. This is done in the timerLoop_Tick event handler:

void timerLoop_Tick(object sender, EventArgs e)
{
    SmokeParticle p;
    ScaleTransform st;
    CreateParticle(new Point(this.Width / 2, this.Height - 20));

    // Update position and opacity of all existing particles
    for (int i = 0; i < LayoutRoot.Children.Count; i++)
    {
        p = LayoutRoot.Children[i] as SmokeParticle;

        if (p != null)
        {
            st = p.RenderTransform as ScaleTransform;
            if (st != null)
            {
                st.ScaleX += fadeSpeed;
                st.ScaleY += fadeSpeed;
                p.Opacity -= fadeSpeed;
                double y = (double)p.GetValue(Canvas.TopProperty);
                p.SetValue(Canvas.TopProperty, y - floatUpSpeed);
                if (st.ScaleX >= 1)
                {
                    LayoutRoot.Children.Remove(p);
                    p.Drift.Stop();
                    particles.ReturnParticle(p);
                }
            }
        }
    }
}

The first thing we do each time around the animation loop is call our function CreateParticle to create a new particle. We then iterate over all of the particles already on the canvas adjusting the position, scale and opacity of each one so that they drify upwards, grow larger and gradually fade out as they go. Once the particle reaches its full size (ScaleX >=1) it is removed from the canvas and returned to the ParticleGenerator for reuse. And that is it - you have a realistic particle smoke effect in Silverlight!

You might also like...

Comments

About the author

John Mannix

John Mannix United Kingdom

Founder and Technical Director of Governor Technology, a web technology company based in London, UK. Interests: .NET, MVC, HTML5, Windows Phone 7, Azure.

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Programs must be written for people to read, and only incidentally for machines to execute.”