1. Game Development
  2. Programming

Make a Neon Vector Shooter With jME: Particle Effects

Read Time:18 minsLanguages:
This post is part of a series called Cross-Platform Vector Shooter: jMonkeyEngine.
Make a Neon Vector Shooter With jME: HUD and Black Holes
Make a Neon Vector Shooter With jME: Warping Grid

We've coded the gameplay, audio, and UI for our jMonkeyEngine-powered Geometry Wars-inspired game, and now we can turn to some neat graphics effects and polish. In this part specifically, we'll focus on particle effects (including some very colorful explosions).


Here's what we're working towards across the whole series:

...and here's a video that shows off the particle effects we're adding in this part:

There will be different types of particles as well as different emitters:

  • When enemies get hit, they will die in a colorful explosion.
  • When the player dies, his ship explodes in a huge golden explosion.
  • The engine of the player emits a simple particle fire effect.
  • Bullets that hit the border of the screen explode.
  • Black holes constantly emit purple particles (so that they look cooler).
  • When a black hole loses hit points, it emits a colorful particle blast.

Besides the last particle type, all particles are affected by gravity and get sucked into black holes. So, when a black hole sucks in many particles at a time, it begins glowing because of all the particles-- which looks pretty cool.

Another effect we'll add is to make our particles get bigger and therefore brighter the faster they are. This will mean that a explosion looks very bright and shiny at first, but quickly loses its brightness once the particles slow down.

In order to achieve our goals, we'll have to add two new classes:

  • ParticleManager: This manager class will take care of the attributes for each kind of explosion.
  • ParticleControl: I think you can already guess that this class, once again, controls the behavior of our particles.

Let's start with the most noticeable effect: exploding enemies.

Enemy Explosions

The first class we need to implement is the ParticleManager class. Since it's responsible for spawning particles, it needs some variables, such as the guiNode, the particleNode and the standardParticle.

We will clone this whenever we need it, but take a look at the basic code:

Integrating the manager in MonkeyBlasterMain is no big deal. We just declare it in the beginning and call the constructor in simpleInitApp():

In order to actually make an enemy explode, we need to have the right method to do this in the ParticleManager:

This method is short, but it does a lot, so we'll go through it step by step.

Coloring the Particles

In order to make our particles more interesting, we'll assign random colors to them.

One method of producing random colors is to choose the red, blue and green components randomly, but this will produce a lot of dull colors and we'd like our particles to have a "neon light" appearance.

We can get more control over our colors by specifying them in the HSV (hue, saturation, and value) color space. We'd like to pick colors with a random hue but a fixed saturation and value, to make them all look bright and shiny, so we need a helper function that can produce a color from HSV values.

Tip: Don't worry too much about how this function works; just understand that it can generate an RGBA color from HSV value. The method is beyond the scope and focus of this tutorial.

Why Do We Need Two Colors?

Now back to our explosion method. Take a look at the highlighted lines:

In order to make the explosion more colorful, we calculate two random colors and interpolate the final particle color randomly for each particle.

Making the Particles Move

The next thing we do is calculate the velocity for each particle. We handle this in an extra method because we want the direction to be random, but not the speed:

First, we generate a random velocity vector and normalize it. Next, we calculate a random speed in the range between 40% and 90% of max.

Now back to the enemyExplosion() method. Here is the part we have not discussed yet:

We clone the standardParticle and set its translation to the origin of the explosion. After that, we interpolate the particle color between the two random ones (as mentioned above). As you can see, we also add a ParticleControl that will control the behavior of the particle. Finally, we need to add the particle to the node for it to be displayed.

Controlling the Particles

Now that our ParticleManager is finished, we need to implement the ParticleControl. Parts of the code will look familiar to you:

At the top of the class, we declare and initialize a few variables; their names should be self-explanatory by now. If you take a look at controlUpdate() you will find familiar code: We move the particle by its velocity, slow it down a bit and rotate it in the direction of the velocity.
If the particle is very slow we set its velocity to Vector3f.ZERO. It's much faster to do calculations with zero than a very small number, and the difference is not visible anyway.

In order to make an explosion really go boom, we will make the particle bigger when it moves fast, which is usually right after the explosion. In the same manner, we make the particle smaller and even transparent when it's moving very slowly or reaches the end of its lifespan. To make it more transparent we call a helper method, setAlpha(float alpha).

Tip: If you don't know how to get children spatials and set their material, you can either just copy-paste the method or have a look at SeekerControl or WandererControl from the second chapter; it's explained there.

Now that we've finished the ParticleControl, you can start the game and see... nothing.
Do you know what we forgot?

Putting it Together

When an enemy dies, we need to call enemyExplosion() in the ParticleManager, otherwise nothing will happen! Take a look at MonkeyBlasterMain and look for the method handleCollisions(), this is where enemies die. Now just insert the call in the right line:

And you must not forget that there is a second way that enemies can die: when they get sucked into black holes. Just insert the (almost) same line a few lines further down when we check for collisions with the black hole:

Now you can finally start the game and play a little. Those particles really add to the atmosphere, don't you think? But let's not stop at one effect; there are many more to come...

Bullet Explosions

When a bullet hits the border of the screen, we will make it explode as well.

Take a look at BulletControl. There is already code that checks whether the bullet is outside the borders of the screen, so let's trigger the explosion there. In order to do that we need to declare the ParticleManager in BulletControl and pass it in the constructor:

Don't forget that you need to pass the particleManager in MonkeyBlasterMain.

We'll insert the call here:

The bulletExplosion(Vector3f position) method is very similar to the enemyExplosion(Vector3f position) method. The only differences are that we won't make the particles quite as fast, and that we use a fixed color (a bright blue). Also, we decrease the lifespan of the particles.

Since we have all the necessary code in place, it's easy to add new explosions, as you can see. Before we add another explosion for the players death, we'll add a new functionality to the ParticleControl.

Repelling Particles From the Screen Borders

When a bullet hits the border of the screen, about half of the particles are useless. Those particles never actually appear on the screen because they fly away from it. Let's change that.

We will now turn the velocity of every particle that leaves the screen around, so that they get 'repelled' by the boundaries.

We don't invert the whole vector, only the x or y variable (depending on the border that was hit). This results in a proper repelling effect, like a mirror reflecting light.

Tip: You must not forget to pass screenWidth and screenHeight from MonkeyBlasterMain to ParticleManager and from there to every ParticleControl. If you don't care about clean code that much you could make two static variables in MonkeyBlasterMain and work with them.

Start the game, and you will notice that bullet explosions look much brighter now. Particles from enemy explosions get repelled as well.

Player Explosion

When the player dies, we want a really big explosion that covers the whole screen. We call the method, once again, in killPlayer() in MonkeyBlasterMain.

The code for playerExplosion is pretty much the same as before. However, this time we use two colors, white and yellow, and interpolate between them. We set the velocity to 1000 and the lifespan to 2800 milliseconds.

Sucking Particles Into Black Holes

Now that we have quite a few particle effects, let's add gravity to them. Whenever they come close enough to a black hole, they should be sucked in—but this is not true for every particle. Later on, we'll want to have a type of particle that does get sucked in and a type that does not. Therefore, we need to add an attribute to our particles:

All particle types that we've created up to now should be sucked in by black holes, so you can add this line to every method in which we spawn particles.

Now to the handling of the gravity. Go to handleGravity() in MonkeyBlasterMain —this is where we implemented the gravity in the third part of the series.

This time, we won't check whether a particle is within reach of the black hole, we'll simply apply the gravity to all of them. If a specific particle is far away, the gravitational effect will not be very strong anyway.

We check whether the particle is affected by gravity and, if it is, we apply it:

Now, we'll need to extend applyGravity() as well:

We need to check the target's name for both Laser and Glow, because those are two different types of particles that will have the same behavior.

Another thing to notice is that we don't only pass on the modified gravity vector, but also the distance to the black hole. This is important in the calculation of the force in applyGravity() in ParticleControl:

Here, gravity is the unit vector pointing towards the black hole. The attractive force is a modified version of the inverse square function.

The first modification is that the denominator is (distance * distance) + 10000—that is, it contains a distance-squared term. This causes the attractive force to approach a maximum value instead of tending towards infinity as the distance becomes very small.

When the distance becomes greater than 100 pixels, (distance * distance) quickly becomes much greater than 10,000. Therefore, adding 10,000 to (distance * distance) has a very small effect, and the function approximates a normal inverse square function.

However, when the distance is much smaller than 100 pixels, the distance has a small effect on the value of the denominator, and the equation becomes approximately equal to:

The second modification we've made is adding a sideways component to the velocity when the particles get close enough to the black hole. This serves two purposes: first, it makes the particles spiral clockwise in towards the black hole; second, when the particles get close enough, they will reach equilibrium and form a glowing circle around the black hole.

Tip: To rotate a vector, v, 90° clockwise, take (v.y, -v.x). Similarly, to rotate 90° counter-clockwise, take (-v.y, v.x).

This particle effect seems pretty when you start the game and look at it, and this is especially true when there are many explosions and particles around. But when there are no explosions, black holes look kind of dull. We'll change that soon.

Spraying Particles Out of Black Holes

In order to make black holes continuously produce particles, we need to take a look at the controlUpdate(float tpf) method in BlackHoleControl. There is an if statement that checks wether the black hole is active; if it is, we'll make it execute this code:

We have a couple new variables here. You need to declare and initialize the long lastSprayTime, the float sprayAngle and the Random rand. Also, you need to declare the particleManager and pass it down from the main class so we can actually spray the particles.

The method will cause the black holes to spray spurts of purple particles that will form a cool glowing ring that orbits around the black hole

The actual sprayParticle() method is nothing special. We create a particle, apply a purple color, add a control and so on:

Start up the game and see how it looks.

Tip: If you want to change the circling behavior of the particles, feel free to play around with the values in applyGravity() in ParticleControl.

This improves the general look of the black holes, but it's not good enough yet! There is one other effect we can add to them...

Black Hole Explosions

Now, we won't make the black holes explode when they die. We will, instead, trigger a particle explosion every time a black hole gets hit.

Add the following method to ParticleManager:

This works mostly the same way as the other particle explosions. One difference is that we pick the hue of the color based on the total elapsed game time. If you shoot the black hole multiple times in rapid succession, you will see the hue of the explosions gradually rotate. This looks less messy than using random colors while still allowing variation.

Ship Exhaust Fire

As dictated by the laws of geometric-neon physics, the player's ship propels itself by jetting a stream of fiery particles out of its exhaust pipe. With our particle engine in place, this effect is easy to make and adds visual flair to the ship's movement.

As the ship moves, we create three streams of particles: a central stream that fires straight out the back of the ship, and two side streams whose angles swivel back and forth relative to the ship. The two side streams swivel in opposite directions to make a criss-crossing pattern, and have a redder color, while the center stream has a hotter, yellow-white color.

To make the fire glow more brightly than it would from bloom alone, we will have the ship emit additional particles that look like this:

Glow Particle
A single glow particle.

These particles will be tinted and blended with the regular particles. The code for the entire effect is shown below:

There's nothing sneaky going on in this code. We use a sine function to produce the swivelling effect in the side streams by varying their sideways velocity over time. For each stream, we create two overlapping particles per frame: one standard particle and a glow particle behind it.

Insert this bit of code in PlayerControl, at the end of controlUpdate(float tpf):

Of course you must not forget to pass the particleManager from MonkeyBlasterMain.


With all these particle effects, Shape Blaster is starting to look pretty cool. In the final part of this series, we will add one more awesome effect: the warping background grid

Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.