Welcome, Guest! Login | Register

Particle Engine tutorial part 1 [Print this Article]
Posted by: Persuter
Date posted: Feb 14 2003
User Rating: 4.2 out of 5.0
Number of views: 12014
Number of comments: 4
Description: Part 1 in 5 part Series
Part 2

Constructing a Half-Life particle engine


(To catch the attention of those simply seeking some cut-and-paste code: There is (hopefully) much in this tutorial that will be useful to any coder. But if you simply want to rip the code out and put it in your tutorial, if you have little patience for reading, as many these days do, the first two parts of this tutorial will have enough code to create the particle engine, and the third has examples of how you might use it in your own mod. You can go through, copy the code, and place it into your own mod. However, it will probably be rather confusing, and you will not be able to exploit it to its full potential. Also, you need to have a working knowledge of C++, otherwise this tut will be useless.)


A particle engine is a wonderful thing to have in any game. It can realistically and quickly simulate explosions, fire, smoke, steam, fragments, etc. However, oftentimes newer game programmers shy away from them, considering them to be "too hard".


On the contrary, however, particle engines lend themselves to simplicity in programming. In this tutorial, I will develop a simple particle engine that can be used in Half-Life and in any other context you might devise. I don't recommend this tutorial for the rank newbie, because I use many terms that you may not understand. There is nothing inherently difficult about the code, but if your mind goes blank and you get big googly eyes when you hear the words "inheritance", "virtual functions", "interface", or "abstraction", you may want to stay away a bit until you become more comfortable with C++ in general. Again, it's not that you won't be able to understand it, but it will probably be confusing to you. (PLEASE take heed of this. This tutorial, again, is NOT copy-paste friendly.)

(Also, a brief note here: A particle system is a conceptual object consisting of multiple particles which interact with each other and the world. I.e., a cloud of smoke would be a particle system, a burst of flame from a gun is another, an explosion a third. A particle engine is a piece of software that manages all these particle systems, displaying them in the right place, causing the particles to interact with the world, and so forth. Thus we are programming a particle engine which will display particle systems. OK, jargon discussion over.)


This tutorial will be divided into five parts, of which this is the first. The main thrust of each part is as follows:


1 - The particle and particle system structures
2 - Managing the particle systems in-game
3 - Creating individual particle systems
4 - Opening your particle engine to many users, such as mappers
5 - Optimizing and extending the particle engine in general


Note that optimizing the particle engine does not come until the last part. This is in line with the general software principle to write code first, optimize later. However, at the end of the third part we will have a reasonably fast particle engine which can be used in any Half-Life mod to great effect. We will not be completely naive in our implementation, but at the end there will be much room for improvement.


So, let's get started. First, we clearly need a data structure to represent the particle. This could be done in any number of ways, using templates, inheritance, etc., but for now we will simply leave it as a simple struct. Starting from the bottom like this will allow us a bit more freedom if we decide to change it later.


To determine what should be in the struct, we first must consider what properties a particle might need. Remember that if there is any property that all particles share, we can leave that out of the struct. We only want to record what is different from particle to particle. We will also for now consider particles as simple points moving through space. Later we will want to extend this, as we will be displaying them as flat sprites.


Clearly we will want vectors recording the position and velocity of the particle. We may well also want an acceleration vector, to represent the constant effects of, say, gravity. We will also need an age variable, to tell us how long this particle has been alive (and thus letting us know when it is appropriate to kill it). We will also need a variable to let us know what the colour of the particle should be, and another to tell us how transparent it should be. For now we'll leave it at that, and extend it a bit when we get to the actual drawing stage.


So, for now we open up a header file called particle.h and type

 CODE (C++) 

struct particle
{
    vec3_t origin;
    vec3_t velocity;
    vec3_t accel;

    int red;
    int green;
    int blue;
    int transparency;

    float age;

    // Lots to add here, so don't... you know, think you're done
};


I here represented red, green, blue, and transparency (which I will also call alpha) as integers because they are seen as such in the Half-Life engine and in most modern-day graphics cards. The ATI Radeon 9700 and the upcoming NV30 cards view them as floats, but for the rest of us, we'll have to stick with boring ol' 24-bit colour (also called true-colour, a name that's made rather silly by 96-bit colour).
OK, so we've got a particle structure. Now we create a particle system structure. All particles are more or less the same, but particle engines really differ here, in how they represent their systems. I want to stress that the way we're going here is not the only or even the best way to go. Object-oriented programming offers so much diversity, and yet it is so often ignored or haphazardly implemented. The particular way I am going to implement this is the LEAST extensible and maintainable way you could POSSIBLY do it. I'll chat about this more in the fifth part of this series, but for now, just be aware that we are going for pure simplicity of programming here, rather than good programming.


Now that we've gotten that out of the way, let's take a look at this horrific way to program a particle system. <!--emo&:)-->user posted image<!--endemo--> Basically, we are going to use a base ParticleSystem class, which we will then inherit from with specific particle systems. This will allow us to create certain generic ParticleSystem classes, such as GravityParticleSystem, which will simulate the effects of gravity on its particles, and then further inherit from them with, for example, RainParticleSystem.


(If you're in any way unclear on the subject of inheritance in classes, this would be a good time to check out some sort of C++ reference on the subject, such as a book or http://www.cplusplus.com.)


Now, what will ParticleSystem need as its inheritable methods? First of all, we will need a function that tests whether the system should remain alive (for example, a rain system will need to check whether it is still raining). Let's call this TestSystem. Then we will need a function that updates the system as a whole, e.g., moves a fireball system in the direction of its velocity, which we'll call UpdateSystem.


So, we have two system-related functions. We must also have some particle-related functions, that is, functions that make up the difference in individual particle simulation between systems. The first two are exactly the same as the system functions, i.e., TestParticle to check if a particle is still alive (for example, perhaps it's hit something), and UpdateParticle to change individual particles. The third particle-related function we'll need is DrawParticle. Now, you might be asking yourself, why do we need a DrawParticle function? Surely it will be the same from system to system? There are two answers. First of all, not necessarily, and it will become clearer why we might need separate DrawParticle functions for separate systems. Secondly, if we want to use this particle engine in another context, such as another game, we will require a wholly new DrawParticle function.

Finally, we must also have a function that the particle system manager will call, which will cause the system to test and update itself, as well as test, update, and draw all its particles. We will call this DrawSystem.

Our class then looks like the following:

 CODE (C++) 

class ParticleSystem
{
public:

 
    BOOL DrawSystem( void );

    virtual BOOL TestSystem( void ) = 0;
    virtual void UpdateSystem( void ) = 0;


    virtual BOOL TestParticle( particle* ) = 0;
    virtual void UpdateParticle( particle* ) = 0;
    virtual void DrawParticle( particle* ) = 0;


// Again, lots to add


};


So far, a simple public interface. However, clearly, a public interface is not all we'll need in this. For example, where do the particles get stored? Here's where we'll run into our first slightly complex data structures and algorithms. Don't worry, it's nothing major.


First up is the concept of a linked list. The problem with using, for example, an array of particles, is that it's not extensible. If we say, for example, particle parts[500];, then we can't possibly use more than 500 particles, and even if we only use 10, we still have 500 in memory. A linked list requires that each member have a pointer to the "next" member in our virtual list. So, the extension to our current particle structure would be the following:

 CODE (C++) 

struct particle
{
    vec3_t origin;
    vec3_t velocity;
    vec3_t accel;

    int red;
    int green;
    int blue;
    int transparency;

    float age;

    particle* nextparticle;

    // Lots to add here, so don't... you know, think you're done
};


Now we allocate memory for particles one at a time, and set each of them to point to the last particle created, thus creating a long list that has one particle at the "head" of the list, which no other particle points to, and one particle at the "tail" of the list, which points to no other particle. We then add a pointer to the head of the particle list in ParticleSystem like so:

 CODE (C++) 

class ParticleSystem
{
public:

    BOOL DrawSystem( void );

    virtual BOOL TestSystem( void ) = 0;
    virtual void UpdateSystem( void ) = 0;

    virtual BOOL TestParticle( particle* ) = 0;
    virtual void UpdateParticle( particle* ) = 0;
    virtual void DrawParticle( particle* ) = 0;

private:

    particle* m_pHeadParticle;

// Again, lots to add

};


Think of this structure as a chain. Each particle is "linked" to the next in this chain. It has a structure like this:

Particle System -> x (head particle) -> x -> x -> x -> x -> x (tail particle) -> NULL

Don't worry if you don't get it, because we're going to add some functions that will help us handle this list. So, we change our ParticleSystem class to reflect this:

 CODE (C++) 

class ParticleSystem
{
public:

    ~ParticleSystem( void );

    BOOL DrawSystem( void );

    virtual BOOL TestSystem( void ) = 0;
    virtual void UpdateSystem( void ) = 0;


    virtual BOOL TestParticle( particle* ) = 0;
    virtual void UpdateParticle( particle* ) = 0;
    virtual void DrawParticle( particle* ) = 0;

    BOOL AddParticle( particle );

private:

    void TrashParticle( particle* );

    particle* m_pHeadParticle;

// Again, lots to add


};


Note that the only functions that deal with the particle linked list directly are not virtual, i.e., they are entirely within the ParticleSystem class. This is by design. All the functions that we will override only deal with the system as a whole, or with individual particles. If we change how the system works (and we will), we don't want all the derived systems to have to be rewritten. That is, we may want to change from a linked list to an array, or maybe even take the linked list out of the system altogether, and just have a global pool of particles that are used. However, we don't want to have to change all the particle systems, we'd prefer to be able to make these changes and have the particle systems behave as they did before (except hopefully faster).

You'll also note that we added a destructor: ~ParticleSystem. This is a very important function: we use it so that we can delete particle system objects and be sure that they cleaned up their mess in memory.

So, let's write the AddParticle and TrashParticle functions first, and then the DrawSystem function will be fairly straightforward.

First, for AddParticle: Note that we take in a particle struct, rather than a pointer to a particle struct. This is, again, because we don't want to have to deal with memory management and garbage collection outside of the system. Thus, you create a particle struct, pass it into the function, and a clone of that particle is created and put onto the list. This will also really help us when we're creating a lot of simple particles that only differ in a few areas, such as velocity. We can create one particle, and then pass it into AddParticle over and over, varying the velocity each time.

It has the return value BOOL because we may want to impose some sort of limit on how many particles can be created, and thus we will want to deny the system the ability to create particles. For now, though, we will always return TRUE.

 CODE (C++) 

BOOL ParticleSystem::AddParticle( particle part )
{

    particle* newpart = new particle;

    (*newpart) = part;

    newpart->nextparticle = m_pHeadParticle;

    m_pHeadParticle = newpart;

    return TRUE;
}


OK, so, to run down these commands in order. First we create a new particle using the new operator. (If you don't know this command, definitely look it up: it is central to the memory management we will do throughout the tutorial.) We then set the new particle structure to be equal to the particle argument. Note the dereferencing of newpart. (*newpart) is a particle structure, which is why we can set it to be equal to part.

Next, we insert it into the linked list. We simply set its next particle to be the head of the linked list, and then set it to be the head. This might seem kind of strange, inserting particles at the head rather than at the end, but it's the most efficient way. To do it graphically:

Imagine O is the new particle, X is the head particle, and x are all the particles after it. So at first we have

ParticleSystem -> X -> x -> x -> x -> x

We then set O's next particle to be X, and we set the ParticleSystem pointer to point to O. Now it looks like

ParticleSystem -> O -> X -> x -> x -> x -> x

Now, TrashParticle is very simple:

 CODE (C++) 

void ParticleSystem::TrashParticle( particle* part )
{
    delete part;
}


It will get more complicated in part 5.

So, now we're ready to do DrawSystem.

 CODE (C++) 

BOOL ParticleSystem::DrawSystem( void )
{
    // We first check to see if the system should remain alive and return false if it should not
    if( TestSystem() )
    UpdateSystem();
    else
    return FALSE;

    particle* part = m_pHeadParticle;
    particle* deadpart = NULL;
    particle* lastpart = NULL;

    // This is the main loop of DrawSystem.
    while( part )
    {  
    // We first test the particle to see if it should remain alive
        if( TestParticle( part ) )
        {
            // If it should we update and draw the particle
            UpdateParticle( part );
            DrawParticle( part );

            // And then move onto the next particle
            lastpart = part;
            part = part->nextparticle;
        }
        else
        {
            // Otherwise we cut the particle out of the list, and trash it.
            deadpart = part;
            part = part->nextparticle;

            if( lastpart )
                lastpart->nextparticle = part;
            else
                m_pHeadParticle = part;

            TrashParticle( deadpart );
        }
    }

    return TRUE;
}


Let's look only at the main loop. We start by setting part equal to m_pHeadParticle. We check that part is not NULL, if it is, we've reached the end of the list. This works nicely even if we have no particles in the list, since if there are no particles, m_pHeadParticle is NULL. As the comment says, we test the particle. If it's still alive we update and draw it. We then set lastpart (meaning the last particle checked) to part, and set part to nextparticle. Now, if part was the tail of the list, nextparticle, by definition, is NULL. This is how we automatically break out of the loop once we get to the end.

If TestParticle returns FALSE, we have to cut the particle out of the list. Now the reason why we had to keep lastpart becomes clear. If we hadn't kept it, its nextpart pointer would now be pointing to a dead particle, which is certainly not the sort of list we have in mind. We set deadpart equal to part, so that we can delete it after we've changed part. We set part to part->nextparticle, since we still want to advance down the list. If lastpart exists, then we set its nextparticle pointer to the current particle. If lastpart does not exist, we are trashing the head particle, and so we must set that pointer to the current particle. We then trash the dead particle.

After we've reached the end of the list, we return TRUE and all is well. (Note that if TestSystem returns FALSE, we return FALSE for the whole system above.) Run through this loop in your mind a few times, get comfortable with it and with linked lists. We will be using this same linked list structure to hold all the systems in the manager as well in the next section. Ideally, you should never have to deal with this code again. However, if there are bugs (and there will be, I found one in the code I use in my mods as I was writing this!), you'll want to be able to fix them. Also, you'll want to be able to modify and extend this functionality. You'll see in the optimization tutorial that we will go from a single linked list of particles to two linked lists of particles, one for active particles and one to hold dead particles to be reused. Moreover, linked lists are a very simple, basic data structure that is used often in many programming contexts, so unless you're planning to restrict your coding to Half-Life alone, it's a good structure to know.

Finally, we want to create the destructor for the particle system. If you don't know what a destructor is, it's a simple function that all classes have, but that you don't have to implement. It is named with a tilde followed by the name of the class, so in our case, ~ParticleSystem. It is called whenever any object of that class is removed from memory, either directly with the delete member, or simply by it going out of scope (if you don't know what that means, don't worry about it, since it is not applicable here). It's generally used with objects that directly declare memory, so we can be sure we cleaned up properly.

It's very simple, though:

 CODE (C++) 

ParticleSystem::~ParticleSystem( void )
{
    particle* part = m_pHeadParticle;
    particle* deadpart = NULL;

    while( part )
    {
        deadpart = part;
        part = part->nextparticle;
        delete deadpart;
    }
   
    m_pHeadParticle = NULL;
}


We simply go through the list, one by one, and delete the particles. Now, whenever we delete a particle system, we can be sure that all its particles have been deleted as well.

Finally, we do have to add one thing. m_pHeadParticle needs to be set to NULL at the beginning of the system for all of this to work. So simply create a constructor function for ParticleSystem. This will get called as well as whatever constructor function you create for your own systems. The class declaration will now look like:

 CODE (C++) 

class ParticleSystem
{
public:
         ParticleSystem( void );
    ~ParticleSystem( void );

    BOOL DrawSystem( void );

    virtual BOOL TestSystem( void ) = 0;
    virtual void UpdateSystem( void ) = 0;


    virtual BOOL TestParticle( particle* ) = 0;
    virtual void UpdateParticle( particle* ) = 0;
    virtual void DrawParticle( particle* ) = 0;

    BOOL AddParticle( particle );

private:

    void TrashParticle( particle* );

    particle* m_pHeadParticle;

// Again, lots to add


};


and the constructor function will be:
 CODE (C++) 
ParticleSystem::ParticleSystem( void )
{
        m_pHeadParticle = NULL;
}


OK, we're done with the particle structure and the ParticleSystem base class for now. Don't be too intimidated by all the text we've had up till now; if you look over the code you'll see it's all pretty simple and straightforward. Email me at persuter@planethalflife.com if you have any questions about the code or the explanations. By the way, this isn't a particle engine yet, we're just setting up the fundamentals, so please don't email me with "How do I get explosions in-game?" until you've read at least to the third tutorial. But if you feel like you've got a pretty good grasp of the linked list structure in particular and in general the manner in which we will create the particle systems, move on!

Part 2

Rate This Article
This article is currently rated: 4.2 out of 5.0 (6 Votes)

You have to register to rate this article.
User Comments Showing comments 1-4

Posted By: infiniteloop on Jan 05 2004 at 19:49:07
Great tut so far. Excellent walkthrough.

Posted By: darthballz on Mar 19 2004 at 22:54:54
The hardest tut ever repeats the stuff over and over. Im not saying that i dont read it. It's confusing if you see the same code over and over. It's probably the most simplelest thing.Edited by darthballz on Mar 19 2004, 22:56:05

Posted By: Unknown on Dec 18 2004 at 05:20:01
very very very nice :)

Posted By: Persuter on Dec 18 2004 at 23:45:12
You know, I can't believe I just noticed this, but you should always declare your destructors as virtual for classes to be derived, i.e., virtual ~ParticleSystem( void ); rather than ~ParticleSystem( void );. As written this tutorial leaks memory all over the place. :P


You must register to post a comment. If you have already registered, you must login.

Latest Articles
3rd person View in Multiplayer
Half-Life 2 | Coding | Client Side Tutorials
How to enable it in HL2DM

By: cct | Nov 13 2006

Making a Camera
Half-Life 2 | Level Design
This camera is good for when you join a map, it gives you a view of the map before you join a team

By: slackiller | Mar 05 2006

Making a camera , Part 2
Half-Life 2 | Level Design
these cameras are working monitors that turn on when a button is pushed.

By: slackiller | Mar 04 2006

Storing weapons on ladder
Half-Life 2 | Coding | Snippets
like Raven Sheild or BF2

By: British_Bomber | Dec 24 2005

Implementation of a string lookup table
Half-Life 2 | Coding | Snippets
A string lookup table is a set of functions that is used to convert strings to pre-defined values

By: deathz0rz | Nov 13 2005


Latest Comments
knock knock
General | News
By: omega | Dec 22 2016
 
knock knock
General | News
By: MIFUNE | Oct 10 2015
 
New HL HUD Message System
Half-Life | Coding | Shared Tutorials
By: chbrules | Dec 31 2011
 
knock knock
General | News
By: Whistler | Nov 05 2011
 
Particle Engine tutorial part 4
Half-Life | Coding | Client Side Tutorials
By: darkPhoenix | Feb 18 2010
 
Particle Engine tutorial part 2
Half-Life | Coding | Client Side Tutorials
By: darkPhoenix | Feb 11 2010
 
Particle Engine tutorial part 3
Half-Life | Coding | Client Side Tutorials
By: darkPhoenix | Feb 11 2010
 
Game Movement Series #2: Analog Jumping and Floating
Half-Life 2 | Coding | Shared Tutorials
By: mars3554 | Oct 26 2009
 
Particle Engine tutorial part 5
Half-Life | Coding | Client Side Tutorials
By: Deadpool | Aug 02 2009
 
Particle Engine tutorial part 5
Half-Life | Coding | Client Side Tutorials
By: Persuter | Aug 02 2009
 

Site Info
297 Approved Articless
8 Pending Articles
3940 Registered Members
0 People Online (8 guests)
About - Credits - Contact Us

Wavelength version: 3.0.0.9
Valid XHTML 1.0! Valid CSS!