Introduction

We're getting close to completion now. If you've been following through the first four parts then well done! We've now looked at getting a window on the screen, building a simple game framework, creating a data model for the game, rendering models, playing sound effects and displaying text and overlays over the game. Our last part is going to add some simple effects to make the whole game feel that bit more glowy!

Particle Engines

The term particle engine is thrown around alot in games development with no one being really clear what it is. For our purposes a particle engine is a data structure that maintains a set of points that are effected by "some external force". These points are then rendered as small quads which appear on the screen as blobs of color. Each particle maintains its position, color and faded-ness (alpha) which can be changed by this "external force". Now, the reason we lump the particles together in an engine is two fold.

First, performance, updating and rendering all the particles together can save us time. For instance, rendering all the particles as one large OpenGL vertex array rather than as individual quads can really speed up rendering. Second, particles can interact with each other and maintaining them from a single location makes this very easy.

When particle engines are implemented generically they can be used for some really nice effects. This flame is generated using a generic particle system with some specific parameters. The same particle engine can be used for lazer fire, smoke and explosions.

The particle engine we're going to create for the asetroids game here is alot simpler than a full generic system. Its going to be used to render the lazers fired by the player and a engine glow from the player's ship.

Particle Groups

The particle engine for our asteroids game is contained within a single class named ParticleGroup. This class is responsible for holding all the information about the particles, updating it as time goes by and rendering the particles themself through OpenGL. First, lets look at the data thats being held in the class for each particle:

/** The positions of the particles being rendered */ private float[][] pos; /** The life left in each particle being rendered */ private int[] life; /** The initial size of each particle */ private float[] initialSize; /** The current size of each particle */ private float[] size; /** The alpha value of each particle */ private float[] alpha; /** The index of the next particle to be used */ private int next; /** The time particles should take to fade out */ private int fadeOut; /** The red component of the colour of each particle */ private float r; /** The green component of the colour of each particle */ private float g; /** The blue component of the colour of each particle */ private float b;

"pos" stores the position coordinates of each particle. The "life" array stores a counter for each particle that indicate how much longer it has to live, i.e. how much longer in milliseconds it will continue to be updated and rendered. "initialSize" and "size" allow us to scale the particles down as they begin to die. The "alpha" value for each particle defines how faded the particle will be rendered - as particles begin to die we fade them out to prevent them flashing out of existence. The "fadeOut" time defines how long it takes particles to disappear. Finally, the "r", "g" and "b" values define the color that are particle group will take on. In this case we're only going to support one color across all particles.

Right, we understand what data we're holding, lets take a look at creating a group of particles:

public ParticleGroup(int count, int fadeOut, float r, float g, float b) { pos = new float[count][2]; life = new int[count]; size = new float[count]; initialSize = new float[count]; alpha = new float[count]; this.fadeOut = fadeOut; this.r = r; this.g = g; this.b = b; }

Note that we're using arrays for the particles and there are fixed number of particles supported by any group. This is more for the potential gains in performance over using something like a dynamic list. The caller only has the ability to specified very simple parameters, count - the number of particles to be held in the group, fadeOut - the amount of time it takes particles to disappear and r,g,b - the colour of the particles that should be rendered.

Initially there are no particles, heres the method that lets the developer add particles at run time:

public void addParticle(float x, float y, float size, int life) { pos[next][0] = x; pos[next][1] = y; this.size[next] = size; this.initialSize[next] = size; this.life[next] = life; alpha[next] = 1; next++; if (next >= this.life.length) { next = 0; } }

So, each time the entity wants to add an entity it must specify the position at which the particle should appear, its initial size and how long its going to last for. We stick these values into the array entries for the next available particle and increment the next available particle. This isn't the prettiest way to handle particle reuse but it'll work fine for the simple systems we're going to use here.

Heres how the Shot fired from the player uses the particle group in its update:

public void update(EntityManager manager, int delta) { // cause the particle to move by calling the abstract super // class's implementation of update super.update(manager, delta); // update the amount of time left for this shot to exist life -= delta; if (life < 0) { // if the life time has run out then remove the shot // entity from the game manager.removeEntity(this); } else { // otherwise add another particle to the engine at the // current position and update the particle engine to // cause existing particles to fade out particles.addParticle(getX(), getY(), size, 200); particles.update(delta); } }

While the shot is still alive we add a particle at the currently location of the shot and then update the particle engine. This gives a lump of particles in a trail behind the shot making it look like a lump of plasma. The way the particles are rendered the more there are in one space the whiter the particles look - this means that there is a white glow around the front of the shot.

The other place we use the particle group is in the Player entity. Here we leave particles behind the ship as it flys giving us a gentle engine trail. Its done like this in the update() method of Player:

if (Keyboard.isKeyDown(Keyboard.KEY_UP)) { // increase the velocity based on the current forward // vector (note again that this is scaled by delta to // keep us framerate independent) velocityX += (forwardX * delta) / 50.0f; velocityY += (forwardY * delta) / 50.0f; // since we're thrusting now we need to add some effect // to our engine engine trail - add a particle to the // engine positioning it just behind the ship float flameOffset = 1.1f; engine.addParticle(positionX-(forwardX*flameOffset), positionY-(forwardY*flameOffset), 0.6f, 150); }

Here we add particles as the player pushes the thrust button. We actually drop the particles just behind the ship to give the feeling they are being pushed out the engine. Again, as the particles congregate around the back of the ship the area gets white glow since the particles all blend together. This is due to the way we blend the particles, lets look at rendering the particle group. First take a look at the renderEngine() method in Player:

private void renderEngine() { // disable lighting for the particles, we want them to // be very glowy GL11.glDisable(GL11.GL_LIGHTING); // When rendering particles we want to blend them together // to give that smoothed look. Note that we're setting the // blend function to GL_ONE which causes the final alpha value // rendered to be ramped up making everything look glowing GL11.glEnable(GL11.GL_BLEND); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); // bind to our texture then ask the particle engine to render // all its particles shotTexture.bind(); engine.render(); // turn blending back off, since we're done now GL11.glDisable(GL11.GL_BLEND); }

Much like the bitmap font rendering we saw in the last part of this tutorial, heres where we handle the texture binding and the blending. However, this time we're binding to a texture that looks like a small star - which is painted across every particle quad we draw. Also, note the blending we're using here "GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);" - this blending means thats we'll combine the alpha from the texture with the value GL_ONE (1). This means that the where the alpha value in the texture get high they'll end being drawn as a value of 1 - which in turn gives us that glowly look to the particles.

Ok, so we've set up the texture and blending function, here's the code in render() in ParticleGroup which actually produces the particle quads:

public void render() { GL11.glBegin(GL11.GL_QUADS); for (int i=0;i 0) { float scalar = alpha[i] / 3; GL11.glColor4f((r * (1 - scalar)) + scalar, (g * (1 - scalar)) + scalar, (b * (1 - scalar)) + scalar, alpha[i]); GL11.glTexCoord2f(0,0); GL11.glVertex3f(pos[i][0]-size[i],pos[i][1]-size[i],-0.3f); GL11.glTexCoord2f(1,0); GL11.glVertex3f(pos[i][0]+size[i],pos[i][1]-size[i],-0.3f); GL11.glTexCoord2f(1,1); GL11.glVertex3f(pos[i][0]+size[i],pos[i][1]+size[i],-0.3f); GL11.glTexCoord2f(0,1); GL11.glVertex3f(pos[i][0]-size[i],pos[i][1]+size[i],-0.3f); } } GL11.glEnd(); GL11.glColor4f(1,1,1,1); }

The code above draws each of the particles as part of a OpenGL quad array. The size and position is read stright from the arrays we hold as part of the group (the z coordinate is constant since we want the quads to face the screen).

The colour of quad is based partly on the color requested by the ParticleGroup creator and partly on the remaining alpha of the particle (the value that goes down as the particle fades out). This alpha value is decremented as the particle gets older causing it to fade and dim as it dies. Here's how that happens in the update() method:

public void update(int delta) { // cycle through every particle, aging it. It the particle // is still alive this frame then fade it and shrink it for (int i=0;i= 0) { life[i] -= delta; if (life[i] < fadeOut) { alpha[i] = life[i] / ((float) fadeOut); size[i] = (life[i] / ((float) fadeOut)) * initialSize[i]; } } } }

When the particle group is updated the particles are cycled through. Each particle is aged based on the amount of time thats passed since the last update. This causes the particles to eventually shrink and fade as we've seen above. Note the "if (life[i] >= 0)" condition. If the particles have no life, they're ignored.

Cool! Now we've got a simple particle engine to fit our needs and its integrated with entities. This last touch makes the game a little more flash and adds a bit of a wow factor.

Conclusion


In this part we've taken a look at adding a purpose built particle system to the game and how that can be used to give the game a little polish. We haven't had to add much code or complexity but the end effect is rounds off the game nicely.


This is also the end of the Asteroids tutorial. Hopefully its covered some useful bits and pieces about Java games development. We've tried to touch on the details that have been asked for including 3D models and game frameworks. If you have any comments or feedback on the tutorial content or style either leave a comment here or drop me a mail at the address below.

Links


Tutorial written by Kevin Glass