The next thing we want to do is discuss a little bit about how particle systems work. A particle system has three major components: particles, a particle emitter, and the system itself. A particle is a small point in your game that will be drawn with an image. A particle often has a position in the game world, as well as a velocity, an angle that it is rotated at, an angular velocity which indicates how quickly the particle is spinning, and usually a color and texture for drawing. In our particle engine, we will have lots of particles, which all add up to produce an interesting effect. Also, particles almost always have some way of determining how long they should live for, so the particles can be removed as new ones are generated.
A particle emitter is essentially the location that the particles are coming from. Often the particle emitter is also responsible for determining how many particles will be created at any given time.
The particle engine manages the state of the previous two components and is responsible for removing dead particles from the system.
Our first task will be to create a Particle
class, which will be used in our particle system.
The first step will be to create the class itself.
To create this class, right-click on your project in the Solution Explorer, and choose Add > New Item from the popup menu.
The Add New Item dialog will appear.
Select the Class template, and change the name (near the bottom) to Particle.cs.
Click on the Add button when you have done this.
The new class file will open up in the main editor window.
The first change we will want to make is to make the class public, so near the top of the file, find the line that says:
class Particle
and change it to say:
public class Particle
We now need to add some properties and instance variables to the class to keep track of important information about our particle. Add the following as instance variables:
public Texture2D Texture { get; set; } // The texture that will be drawn to represent the particle
public Vector2 Position { get; set; } // The current position of the particle
public Vector2 Velocity { get; set; } // The speed of the particle at the current instance
public float Angle { get; set; } // The current angle of rotation of the particle
public float AngularVelocity { get; set; } // The speed that the angle is changing
public Color Color { get; set; } // The color of the particle
public float Size { get; set; } // The size of the particle
public int TTL { get; set; } // The 'time to live' of the particle
All of these parameters should make sense to you, except perhaps the TTL
property, which is short for “time to live”.
Essentially, this variable will count down to zero, at which point, the particle is dead.
The constructor is simply going to take each of these values in and assign them to their respective fields, as shown below:
public Particle(Texture2D texture, Vector2 position, Vector2 velocity,
float angle, float angularVelocity, Color color, float size, int ttl)
{
Texture = texture;
Position = position;
Velocity = velocity;
Angle = angle;
AngularVelocity = angularVelocity;
Color = color;
Size = size;
TTL = ttl;
}
Update()
MethodThe Update()
method has a few things that it needs to worry about.
First, it needs to update both the position and angle by the velocity and angular velocities respectively.
Additionally, it needs to decrement the TTL (time to live) variable to move it one step closer to expiring.
You can do this by adding the following code to your Particle class:
public void Update()
{
TTL--;
Position += Velocity;
Angle += AngularVelocity;
}
Draw()
MethodThe Draw()
method only needs to draw the particle’s texture with all of the necessary properties, which is done with the code below:
public void Draw(SpriteBatch spriteBatch)
{
Rectangle sourceRectangle = new Rectangle(0, 0, Texture.Width, Texture.Height);
Vector2 origin = new Vector2(Texture.Width / 2, Texture.Height / 2);
spriteBatch.Draw(Texture, Position, sourceRectangle, Color,
Angle, origin, Size, SpriteEffects.None, 0f);
}
We start off by doing a couple of quick calculations for the source rectangle (which we want to cover the entire texture) and the origin, which is the center of rotation, we want to be at the center of the texture.
We then draw the texture scaled, rotated, colored, and placed according to the information of the particle.
Notice that we don’t call spriteBatch.Begin()
or spriteBatch.End()
here, because we are going to be drawing a lot of these all at one time.
We will call Begin()
and End()
later when we are writing the drawing code for the entire particle engine.
Also, don’t forget to add the following two using
directives so that your code will compile correctly to the top of your file:
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
That should get you a complete Particle
class, and in part 3, we will move on and design our ParticleEngine
class. Below is the entire code for this class, all assembled together:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
namespace ParticleEngine2D
{
public class Particle
{
public Texture2D Texture { get; set; }
public Vector2 Position { get; set; }
public Vector2 Velocity { get; set; }
public float Angle { get; set; }
public float AngularVelocity { get; set; }
public Color Color { get; set; }
public float Size { get; set; }
public int TTL { get; set; }
public Particle(Texture2D texture, Vector2 position, Vector2 velocity,
float angle, float angularVelocity, Color color, float size, int ttl)
{
Texture = texture;
Position = position;
Velocity = velocity;
Angle = angle;
AngularVelocity = angularVelocity;
Color = color;
Size = size;
TTL = ttl;
}
public void Update()
{
TTL--;
Position += Velocity;
Angle += AngularVelocity;
}
public void Draw(SpriteBatch spriteBatch)
{
Rectangle sourceRectangle = new Rectangle(0, 0, Texture.Width, Texture.Height);
Vector2 origin = new Vector2(Texture.Width / 2, Texture.Height / 2);
spriteBatch.Draw(Texture, Position, sourceRectangle, Color,
Angle, origin, Size, SpriteEffects.None, 0f);
}
}
}