Welcome, Guest! Login | Register

Particle Engine tutorial part 4 [Print this Article]
Posted by: Persuter
Date posted: Apr 04 2003
User Rating: 5 out of 5.0
Number of views: 10506
Number of comments: 1
Description: Part 4 in 5 part series
Part 3
Part 5

Alright, so now you've got a working particle engine in your mod, you've shown off some pictures and whatnot, and your mappers are clamouring to get at it. In this tutorial we will explore making a simple map entity that will create a single particle system. This can be very useful. For example, you could use it as the fire of a torch, or steam coming through a grate.

So, first, obviously, if we want the mapper to be able to place an particle-emitting entity, we need to create the entity. So, let's create a new file in the server workspace calledParticleEmitter.h, and put the following in it:

 CODE (C++) 
class CParticleEmitter : public CBaseEntity
{
public:

    void Spawn( void );
    void Precache( void );
    void KeyValue( KeyValueData* );

    void MakeAware( entvars_t* );

    int type;
};


Nothing difficult here, the only strange thing we have is MakeAware. The reason we need MakeAware is because when people log onto a server, they must begin displaying the appropriate particle systems. We can't just spawn these entities at the beginning and have them tell all the players to start displaying them, because most players will not be logged on yet.

We now create ParticleEmitter.cpp in the server workspace. First, we need to include the correct header files.

 CODE (C++) 
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "particleemitter.h"


That's pretty simple. Those are the three most basic header files, plus our own definition.

Next, we want to set up the basic functions that we need for the entity.

 CODE (C++) 
LINK_ENTITY_TO_CLASS( func_particle, CParticleEmitter );

void CParticleEmitter::Spawn( void )
{
    Precache();
}

void CParticleEmitter::Precache( void )
{
}


OK, so we now have an entity that will spawn into the game with the entity type func_particle (feel free to make up your own type). However, this doesn't really help mappers, since they can't yet define what particle system will be attached to this entity.

 CODE (C++) 
void CParticleEmitter::KeyValue( KeyValueData* pkvd )
{
    if ( FStrEq(pkvd->szKeyName, "system") )
    {
        type = atoi( pkvd->szValue );
        pkvd->fHandled = TRUE;
    }
    else
        CBaseEntity::KeyValue( pkvd );

}


KeyValue is a function that gets mapper-defined variables from the map itself, before the entity is actually spawned. Here, we get the type of system that the mapper desires to be here. OK, so now the game knows what kind of particle system you want, and it knows where it is. Now we need to tell the client these things. We do this in MakeAware.

 CODE (C++) 
extern int gmsgStartParticle;

void CParticleEmitter::MakeAware( entvars_t* pevToBeNotified )
{
    MESSAGE_BEGIN( MSG_ONE, gmsgStartParticle, NULL, pevToBeNotified );
        WRITE_BYTE( type );
        WRITE_COORD( pev->origin[0] );
        WRITE_COORD( pev->origin[1] );
        WRITE_COORD( pev->origin[2] );
    MESSAGE_END();
}


As you can see, we take a pev and send a message to it (and only it, which is what MSG_ONE means), telling what kind of system this is and its origin. Where is MakeAware called, you ask? You'll need to find what gamerules you're using for your mod (you'll probably want to make your own, but if you didn't, you're probably using CHalfLifeTeamplay or CHalfLifeMultiplay. Find the function InitHUD and put the following in it:

 CODE (C++) 

CParticleEmitter* pEntity = NULL;
while( (pEntity = ((CParticleEmitter*)(UTIL_FindEntityByClassname( pEntity, "func_particle" )))) != NULL )
{
    pEntity->MakeAware( pPlayer->pev );
}


You must also put #include "particleemitter.h" at the top of the gamerules file. Now, whenever a player logs in, InitHUD is called with a pointer to their player structure. You simply iterate through the particle emitters and send off a message to the client letting him know what to run.

Now, if you don't know much about message-passing, you may be saying, �But wait! Where did gmsgStartParticle get defined? Good question. We have to create the message before we can use it. First we go to player.cpp, around line 189, and add the following line to the long list of gmsg declarations:

 CODE (C++) 
int gmsgStartParticle = 0;


Then, in LinkUserMessages, directly below it, we add:

 CODE (C++) 
gmsgStartParticle = REG_USER_MSG( "StartPart", -1 );


The -1 tells the engine that we don't know how big the message is. In truth, we do, it's 13 bytes. But maybe we'll change it, so I just go ahead and put a -1 to make things simpler. We won't be sending these messages enough to really make a difference (the engine has to count the bytes and send a length header, which is a pretty minor addition). That's all we have to do to add a message on the server side. We now need to go over to the client side and add a message handler to actually start the particle systems.

First, in CHud::Init, around line 297, add:

 CODE (C++) 
HOOK_MESSAGE( StartPart );


Next, above that function, around line 260, add the following function:


 CODE (C++) 
int __MsgFunc_StartPart(const char *pszName, int iSize, void *pbuf)
{
    if (gViewPort)
        return gViewPort->MsgFunc_StartPart( pszName, iSize, pbuf );
    return 0;
}


This is all boilerplate, by the way, just the standard stuff you add to make server-client messages work. Next, we go to vgui_TeamFortressViewport.h, and in the class declaration, around line 374, in the section marked �Message Handlers�, we add the function declaration:


 CODE (C++) 
int MsgFunc_StartPart( const char *pszName, int iSize, void *pbuf );


Note that this is not the same function we just created, we are going to have two functions with very similar names. The function we just created calls this function through gViewPort.

Now, we go into vgui_TeamFortressViewport.cpp and add (anywhere you want, but around line 1900 will probably work well enough):

 CODE (C++) 
int TeamFortressViewport::MsgFunc_StartPart( const char *pszName, int iSize, void *pbuf )
{

    BEGIN_READ( pbuf, iSize );

    vec3_t origin;
    int type = READ_BYTE();

    origin[0] = READ_COORD();
    origin[1] = READ_COORD();
    origin[2] = READ_COORD();

    switch( type )
    {
    default:
    break;
    }

    return 1;
}


OK, we're done! The more acute among you may have noticed that there is no actual code here. As you add more particle systems, you will have to add cases that will look something like this (for a steam emitter):

 CODE (C++) 
switch( type )
{
case STEAM:
    GetManager()->AddSystem( new SteamParticleSystem( origin ) );
default:
    break;
}


where STEAM is defined as whatever you defined it as in the fgd. Speaking of which, you'll also need to go into your mod's fgd and create a func_particle entity with an integer variable called system. I don't think I'm going to specify how to do that, as it's pretty simple, just look at the entities in halflife.fgd. You need to know how to create entities in an fgd anyway.

That's it. If you want to continue by reading the fifth tutorial, where you will gain some extra speed optimizations and some pointers to where you might want to go now, that's fine, but otherwise, you should have a working particle system and a way to allow your mappers to put particle emitters in the game. Note that if you want to put particle systems that aren't emitter types into the game, that's easily done on the client side with the same GetManager()->AddSystem function used above. For example, in EV_HLDM_DecalGunshot, you might add GetManager()->AddSystem( new StoneWallSpark( pTrace->endpos, pTrace->plane.normal ) ), where StoneWallSpark is a particle system that showers sparks off a wall. But this is all stuff you will have to figure out on your own. Thanks for reading. As always, email me at persuter@planethalflife.com if you have any comments, suggestions, or corrections.

Part 3
Part 5

Rate This Article
This article is currently rated: 5 out of 5.0 (1 Votes)

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

Posted By: darkPhoenix on Feb 18 2010 at 02:54:16
Great stuff!

I ended up creating an env_particle rather than a func_particle because it seemed to be the way to go (until I figure out how to read a func_particle brush's coordinates, anyway.) Now I need to add a few more things to it -- area of effect, strength, direction, etc -- but that should all be fairly straightforward. Even making it switchable might be nice; I've already thought of a few applications for that (and so far I've really only been thinking SNOW and RAIN!)

The biggest problem I've run into is that, as presented, the system doesn't detect change of level. (Compiled an env_particle into one of my levels, showed it off to everyone, then switched to a different level to demonstrate how too much snow can affect the FPS, and found it was still snowing in that level. From about the same coordinates... D'oh! :-)) Still, I know how to fix that -- or I know where to start looking, anyway! :-)

Thanks!


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!