You’ve heard of elf on a shelf, now get ready for a particle article.
Particles are a great way to add style, colour, and flair to your 2D games. Whether you’re looking to create an effect that’s visually dazzling or soft and subtle, GameMaker’s particle systems can help you achieve it.
What is a particle?
A particle is a graphic resource that has certain fixed properties which are defined by a particle system.These properties cannot be manipulated directly for individual particles, but are changed through the code that is used to define the system that a type of particle belongs to.
Particles can be used to create beautiful and flashy or subtle and discreet effects in a game without the CPU overhead that instances can cause.
Fire effect made with particles, courtesy of Martin Crownover
What is a particle system?
Think of a particle system as a container that holds our particles ready for use.
We use code to define a series of visual aspects for our particle, and then we place it in the "container" so that we can take it out and use it whenever we need it.
How do I set up a particle system in GameMaker?
Before setting up a particle system, it's important to note that most of a particle systems code is only ever called once in a game, usually in some type of controller object in the first room or level (this is not always the case, but it’s a general rule of thumb).
This is because a particle system, once created, stays in memory and is ready to be used at any time. If you create it more than once, it can quickly increase the memory usage and eventually cause serious lag at best, or crash your game at worst.
Since you’d probably like to avoid those kinds of calamities, we’ll be showing you how to make a global particle system that can be used by any object at any time.
PART I - CREATING A PARTICLE SYSTEM
To start, we'll need a controller object for the particle system. We’ll also need to define the system properties and give it a name so we can use it later.This particle controller object will be placed in the first room of your game. To keep things simple, we’ll place the following code in the Game Start event.
Since this is going to be a global system that any object can use, we’ll set it up like this:
Notice that the particle system is given a layer.Code
global.P_System=part_system_create_layer(layer, true);
GML Visual
In the code above. we’re assigning it to the layer that the instance running the code is on, but you could as easily give it to any layer present in the room that the controller instance will be placed in.
It's good practice to create a layer specifically for the particle system and then place your controller instance on that (or assign the layer when you create the system).
Note that we also flag the system as persistent. This is because we want it to persist across all rooms without having to recreate it again. If you set this to false, trying to access the system using the global variable will cause an error.
That's the system created then, but what about the particles? Until we define them, the system is basically useless.
To create a particle, you have to define its general properties. These are like object properties, but they only apply in a general way to individual particles.
What this means is that if you give the particles a minimum movement speed of one and a maximum movement speed of two, any particle created by the system will have a random speed between one and two pixels per step, and an average speed of 1.5.
Let's name and add our first particle to the system. The following code should go in the Room Start event of the same object we used to create the particle system:
Before we go on, it’s worth noting that the GML Visual action permits you to enable or disable additive blending for the particle when it’s drawn to the screen. We'll explain what enabling this means in a little while.Code
global.Particle1 = part_type_create();
GML Visual
So, we’ve created a particle type, but we still need to define its general properties (ie: how it looks, its speed, rotation, alpha etc.).
How to define a particle's properties
We can use a number of different functions to define a particle effect:
part_type_shape(ind, shape)
- Sets the shape of the particle type to any of the constants above (default ispt_shape_pixel
).part_type_size(ind, size_min, size_max, size_incr, size_wiggle)
- Sets the size parameters for the particle type. You specify the minimum starting size, the maximum starting size, and the size increase in each step (use a negative number for a decrease in size).part_type_scale(ind, xscale, yscale)
- Sets the horizontal and vertical scale. This factor is multiplied alongside the size parameters we just mentioned. It’s particularly useful when you need to scale differently in x- and y-direction.part_type_color3(ind, colour1, colour2, colour3)
- Indicates which three colours the particule will fade between over the particle’s lifetime.part_type_alpha3(ind, alpha1, alpha2, alpha3)
- Sets three alpha transparency parameters (0-1) for the particle type, which the particle will merge between over its lifetime.part_type_speed(ind, speed_min, speed_max, speed_incr, speed_wiggle)
- Sets the minimum and maximum speed properties for the particle type. A random value between your minimum and maximum speeds is chosen when the particle is created. You can indicate a speed increase in each step. Use a negative number to slow the particle down (the speed will never become smaller than 0).part_type_direction(ind, dir_min, dir_max, dir_incr, dir_wiggle)
- Sets the direction properties for the particle type. Again, you specify a range of directions in counterclockwise degrees (i.e. 0 indicates motion to the right). To let the particle move in a random direction, choose 0 and 360 as values. You can specify an increase in direction for each step.part_type_orientation(ind, ang_min, ang_max, ang_incr, ang_wiggle, ang_relative)
- Sets the orientation angle properties for the particle type. You specify the minimum angle, the maximum angle, the increase in each step, and the amount of wiggling in the angle. You can also indicate whether the given angle should be relative (1) to the current direction of motion or absolute (0). By setting all values to 0 but ang_relative to 1, the particle orientation will precisely follow the path of the particle.part_type_blend(ind, additive)
- Sets whether to use additive blending (1) or normal blending (0) for the particle type.part_type_life(ind, life_min, life_max)
- Sets the lifetime bounds for the particle type.
What is a particle wiggle?
A particle speed minimum of 2 and maximum of 5 with a wiggle of 20 will oscillate very quickly between the min/max speeds for the lifetime of each particle.
What is a particle lifetime?
A lifetime is the length of time, measured in game steps, that a particle will exist for.
So, a lifetime of 30 min and 30 max will have the particle existing for exactly 30 steps, but a lifetime of 20 min and 60 max will have each particle exist for a random number of steps between 20 and 60.
What are particle blend options?
If you’re using GML Visual, particle blending is enabled or disabled when you create a particle type, as we discussed earlier. With this, you can set the particle to have additive blending (enabled) or normal blending (disabled).
Having additive blending means that a particle's brightness will be added to whatever appears behind it on the screen, such as sprites, backgrounds, or even other particles. This creates a saturated glowing look that’s useful for fire and magic effects, for example, but not so useful for things like smoke.
Experiment to see the differences when creating your own particles, but keep in mind that additive blending will normally increase the texture swaps that render the game, which in turn can cause lag if you use too many.
To define our global particle using the functions we detailed above, we'd add something like this (again, in the Room Start event of the controller, where we defined the particle system):
Code
part_type_shape(global.Particle1, pt_shape_flare);
part_type_size(global.Particle1, 0.01, 0.05, 0, 0.5);
part_type_color3(global.Particle1, c_aqua, c_lime, c_red);
part_type_alpha3(global.Particle1, 0.5, 1, 0);
part_type_speed(global.Particle1, 2, 5, -0.10, 0);
part_type_direction(global.Particle1, 0, 359, 0, 20);
part_type_blend(global.Particle1, true);
part_type_life(global.Particle1, 30, 60);
GML Visual
PART 2A: CREATING PARTICLES DIRECTLY
There are a couple of ways to create particles, and each has its pros and cons. You can use emitters to burst or stream particles, or you can create particles directly at a point in the room.Which one you use really depends on what effect you’re trying to create, but we'll start with the easiest of the two, which is creating particles directly without the use of an emitter.
You can use the following steps in any event where you want to make particles appear (step event, a collision event, a mouse pressed event, etc.):
CodeThat short string of code will create 50 particles of Particle1 from our global system at the position of the mouse cursor (you can see this being used in the Global Mouse Down event, linked at the end of this article).part_particles_create(global.P_System, mouse_x, mouse_y, global.Particle1, 50);
GML Visual
The great thing about that line of code is that it can be used anywhere without any fuss. For example, if you have a rocket, you could place code like this in the step event to create smoke particles at every step, or add it into a collision event and have a damage effect burst when the rocket is hit.
You can also use this to create particles over an area by changing the x/y coords randomly, for example:
Coderepeat(50)
{
var _mx = mouse_x - 50 + irandom(100);
var _my = mouse_y - 50 + irandom(100);
part_particles_create(global.P_System, _mx, _my, global.Particle1, 5);
}
GML VIsuals
The code above will create 250 particles at a random position within a 100px square area around the mouse cursor position.
PART 2B: CREATING PARTICLES WITH EMITTERS
Since we've explained the easy way to create particles, let's now go the slightly more complex but versatile way, which is to use emitters.Emitters are another aspect of the particle system that has to be defined before being used, so we'll make a global emitter the same as we did for the system and the particle type.
We also have to decide whether to have a static (non-moving) emitter, whether we’re going to burst or stream the particles, and what area and distribution we’re going to have the emitter use.
Types of particle emitters
A static emitter is one that you only need to define once. This is useful for something like a log fire - it’s not going anywhere, and all we need it to do is crackle like the good little fire it is.
However, a fireball would require a dynamic emitter, since we’d want it to move across the screen.
As for bursting or streaming, a burst is a one-off explosion of particles, whereas a stream is just that - a constant stream of particles every game step.
You can also use emitters to define an area for emitting particles (which can be a rectangle, ellipse, diamond, or line) as well as the distribution curve (gaussian, inverse gaussian, or linear).
The following images illustrate the different types of shape available:
The distribution curves are illustrated here:
Here’s a set of example codes that define two emitters.
One will be static and stream particles over the area of the whole room, while the other will follow the mouse and burst every 30 steps from a small ellipse.
The first code block should go in the Game Start Event along with the rest of the code that defines the system:
Code
global.Emitter1 = part_emitter_create(global.P_System);
global.Emitter2 = part_emitter_create(global.P_System);
part_emitter_region(global.P_System, global.Emitter1, 0, room_width, 0, room_height, ps_shape_rectangle, ps_distr_invgaussian);
part_emitter_region(global.P_System, global.Emitter2, mouse_x - 50, mouse_x + 50, mouse_y - 25, mouse_y + 25, ps_shape_ellipse, ps_distr_gaussian);
part_emitter_stream(global.P_System, global.Emitter1, global.Particle1, 10);
alarm[0] = room_speed;
GML Visual
The first emitter will now constantly stream particles every step, and the second emitter has been setup, but does nothing. Since we want it to be a dynamic emitter and burst particles every second, we need to add an Alarm[0] event:
Code
part_emitter_region(global.P_System, global.Emitter2, mouse_x - 50, mouse_x + 50, mouse_y - 25, mouse_y + 25, ps_shape_ellipse, ps_distr_gaussian);
part_emitter_burst(global.P_System, global.Emitter2, global.Particle1, 10);
alarm[0] = room_speed;
GML Visual
Here, we change the position of the emitter based on the current mouse cursor position, and then we burst some particles before resetting the alarm.
You can call the emitter region functions at any time and use them to create some complex effects by tweening values for their area and position. All particles created after each change will follow the new settings.
But what if you want several objects to emit particles at the same time?
A global emitter can only be in one place at a time, so you would need to create local emitters in each object. These emitters will still use the global particle system and any particles that are within it, but they can be different shapes and sizes and move with the object, making them ideal for rockets, bullets, or anything else that moves.
You can use the same code we used above, but without the global. before the emitter names (if you drag room1 in the example file to the top of the room resources above room0 and run the game, you can see examples of this).
That's the creation of particles dealt with, but there is one more thing that's very important: clean-up!
PART 3: CLEANING UP
As we mentioned earlier, once created, a particle system (and its particles, emitters etc.) is stored in memory for instant use. But what happens when you restart the game? Or if your player dies and starts the room again?
Well, if you don't manage the code right, you get a memory leak.
This is when something starts eating up memory because it’s been created and then dereferenced (ie: the variable that stored it no longer exists, so the game no longer has access to it). This will cause lag or even block the computer causing your game to crash.
It’s a pretty common problem with first-time users of particle systems, but GameMaker allows you to delete particle systems and their emitters, as well as particles from memory when they’re not in use.
First of all, you need to decide where you’re going to create the system and how you’ll use it. You could create it in the Game Start event of an object that’s in your first room (like a menu or title screen), but this means that if you restart the game code using the game_restart() function, it’ll be recreated and cause a memory leak.
To avoid that happening, you’d need to have something like this in the Game End event of your object:
Codepart_type_destroy(global.Particle1);
part_emitter_destroy(global.P_System, global.Emitter1);
part_emitter_destroy(global.P_System, global.Emitter2);
part_system_destroy(global.P_System);
game_restart();
GML Visual
This will remove the defined particle, the emitters, and the system from memory before restarting the game again.
If you have it in a controller object that’s placed in every game room, you could take the create event that defines the particle system and other parts, then place the destroy codes in the room end event.
Whenever you start thinking about the most suitable place to create your particle system, don’t forget to consider the most suitable place to destroy the system, as well.
You now know the basics of particles, particle systems, and particle emitters.
Before you rush off to beautify your games with all your new-found knowledge, it’s worth remembering that while particles are less CPU-hungry than objects, but they’re not always the solution. No matter how dazzling the particle effect, you don’t want them to make your game lag or crash.
We also recommend checking out one of the many particle designing tools that are available, such as:
- Geon FX, useful for visually creating and editing particle effects
- Sparks, a particle animation tool that will create sprites from particle effects (useful for HTML5 and other occasions where particles cannot be used).
Happy gamemaking!