Categories
spaceslingers spaceslingers devlog tutorial

Tutorial – Creating Sweet Particle Effects in GMS2

Learn how to create the wormhole particle effects in Spaceslingers, along with a bunch of other particle effects.

A few people liked the look of my wormhole particles in Spaceslingers, so I figured I’d write a little guide on my process for making particles in GMS 2, including both code for the wormhole particles and how I go about experimenting to get nice effects. By the end, you’ll have recreated the wormhole particles exactly as I’ve shown them below, and should also have a bit of an idea about how to create new particle effects for yourself.

Download the .yyz project file and import it into GMS2 (File > Import Project > Select the sweet-particle-effects.yyz file wherever you downloaded it to) to have easy access to the code and mess around with the particles yourself. Please note, I have altered some of the particle settings a little bit for the project file, to make them display better. Here’s a preview of it:

Contents

PART 1: MAKING YOUR FIRST PARTICLE

PART 2: A PARTICLE PRIMER

PART 3: WARPING SPACETIME

PART 4: CONNECTING THE DOTS

PART 5: THE AGE OF EXPERIMENTATION

PART 6: WHERE DO WE GO FROM HERE

part 1: Making Your First Particle

First, let’s see what the wormhole particles look like (after being embiggened somewhat):

So, first things first, what do we need to start creating particles? We’ll need a particle system. Let’s get that going. Open up the Create Event of the object you want to spawn the particles in and type in:

// ** Particle System: wormhole_ps_upper **
wormhole_ps_upper = part_system_create();
part_system_layer(wormhole_ps_upper ,layer);
part_system_automatic_draw(wormhole_ps_upper ,false);

Now, what we’ve done there is create a particle system called wormhole_ps_upper (I often create two separate particle systems, one to be drawn below the instance and one to be drawn above, which is why there’s the _upper postfix on there, but we don’t need two for this effect). Then we make sure the particle system is on the correct layer (setting it to layer simply makes sure the particle system is on the same layer as the instance creating it) and finally, we cancel automatic drawing of the particle system. This is because it gives you a little more control over exactly which depth the particles will appear. A particle system is necessary in order to create particles.

Now we’ll need to setup one particle emitter (the actual wormhole particles can be created without an emitter, but the line connecting the two needs one).

// ** Particle Emitter : wormhole_connect_emit **
wormhole_connect_emit = part_emitter_create(wormhole_ps_upper);

That’s nice and simple, just creating a particle emitter called wormhole_connect_emit and linking it to wormhole_ps_upper.

So far, we haven’t actually created any particles. That’s about to change. Let’s create our first particle:

// ** Particle : wormhole_part **
wormhole_part = part_type_create();

Now let’s mosey on over to the Step Event and add in a quick bit of code to create those particles in-game:

part_particles_create(wormhole_ps_upper, mouse_x, mouse_y, wormhole_part, 10);

And then pop open the Draw Event and enter this code to get the particles to draw:

part_system_drawit(wormhole_ps_upper);

As a side note, the reason that I manually draw the particles is because you can then position exactly where the particles will be drawn, layer-wise. For example, I could do this:

draw_sprite(sprite1,0,x,y);
part_system_drawit(wormhole_ps_upper);
draw_sprite(sprite2,0,x,y);

And the particles will be drawn above sprite1 and below sprite2. If you didn’t want to do this, you can just remove the line part_system_automatic_draw(wormhole_ps_upper ,false); from the Create Event and the particles will automatically draw themselves.

Run the game and tada! We now have particle effects. But they look pretty damn bad:

We have to give the particle some properties to make it look better.

part 2: A Particle Primer

Particles come with a bunch of functions to change their properties. Naively, these properties are set in stone once the game has run these functions, but we can change them on the fly if we really need to do that for the functionality we want. However, for the basic wormhole particle, we can set everything once and forget it. Let’s open up the Create Event again, go to the bottom of the code we’ve added so far and chuck this into the mix:

part_type_size(wormhole_part,0,0,0.01,0);
part_type_scale(wormhole_part,1,1);
part_type_life(wormhole_part,60,60);
part_type_gravity(wormhole_part,0,0);
part_type_speed(wormhole_part,4,4,-0.07,0);
part_type_direction(wormhole_part,0,359,4,0);
part_type_orientation(wormhole_part,0,0,0,0,0);
part_type_blend(wormhole_part,1);
part_type_alpha3(wormhole_part,1,1,0);
part_type_color2(wormhole_part,make_color_rgb(255,0,102),make_color_rgb(0,204,255));
part_type_sprite(wormhole_part,part_wormhole_glow,0,0,0);

So that’s a lot to take in as a whole, but each part_type_*() function is relatively self-explanatory if you read the function name. Just to make sure we’re all on the same page, I’ll go through each function, explain the arguments used and also explain a little bit about why my settings are what they are for each (I’m also going to skip listing the first argument for each function as it is always simply the name of the previously created particle you want the settings to be applied to). If you already know everything there is to know about particle functions, you can skip to PART 3: WARPING SPACETIME (but also, why are you reading this? ;P ).


part_type_size(wormhole_part,0,0,0.01,0);

Arguments

  • The minimum size the particle can be.
  • The maximum size the particle can be.
  • The amount your particles size will change each step.
  • Whether the particle should “wiggle” in size (or oscillation between growing and shrinking).

We want the particles to start at nothing, and grow (you can see in the wormhole gif at the top of the tutorial, the particles start at the center with no size, then move outwards while rotating and growing.


part_type_scale(wormhole_part,1,1);

Arguments

  • The x scale of your particle.
  • The y scale of your particle.

It’s subtly different from the previous function as it lets you stretch and squash your particles, rather than simply changing the overall size. I don’t need any deforming of the particles, so the arguments are simply 1.


part_type_life(wormhole_part,60,60);

Arguments

  • Minimum life of your particle.
  • Maximum life of your particle.

These are measured in steps (or frames, if that’s an easier way to think about it). So in a game that has a room speed of 60, this particle will last 1 second (room speed of 60 frames per second, the particle lasts for 60 frames). Tweaking these settings to lower numbers will shrink the border of the wormhole (as the particles die quicker, meaning they can’t travel outwards as far), but increasing it will cause the particles to start spiraling back inwards. This is because, as we’ll see later, the particles direction curves slightly, so the longer they last the more time they have to turn enough to start heading back in the direction they came. There’s a sweet spot for effects like this and it often takes a bit of experimentation with different numbers to find the right combination.


part_type_gravity(wormhole_part,0,0);

Arguments

  • The amount of gravity to be applied.
  • The direction the gravity should be applied in.

We don’t want gravity for this particle, so it’s just set to 0.


part_type_speed(wormhole_part,4,4,-0.07,0);

Arguments

  • Minimum speed the particle will start at.
  • Maximum speed the particle will start at.
  • The amount the speed will change each step.
  • The “wiggle” (or oscillation between increasing and decreasing) of the speed.

To get a solid “ring” around the edge of the wormhole, we need each particle to start moving at exactly the same speed, so minimum and maximum are set to the same value (if they were different, some particles would move further and the ring would become more “fractal”, increase the difference by enough and eventually the illusion of a ring would fall apart completely). Increasing both will push the ring further outwards. We also need the particles to decrease in speed over time, as this gives the nice illusion of some sort of “force” pushing from the center outwards (the further from the point of force, the slower each particle should be moving).


part_type_direction(wormhole_part,0,359,4,0);

Arguments

  • Minimum direction a particle can pick from.
  • Maximum direction a particle can pick from.
  • The change in direction each step (basically, this makes the particle “curve” through space as it’s moving).
  • The “wiggle” (or oscillation between a positive curve and a negative curve) in direction.

We want our particles to be moving out in all directions, and slightly curving, to give that feeling of rotating out of a vortex, so we let it choose between all directions and make it increase it’s direction by 4 degrees each step.


part_type_orientation(wormhole_part,0,0,0,0,1);

Arguments

  • Minimum orientation the particle can choose.
  • Maximum orientation the particle can choose.
  • The increase in orientation each step.
  • The “wiggle” (or oscillation between positive rotation and negative rotation) in orientation.
  • Whether the orientation is relative to the direction or not.

So orientation is the actual direction the particle is facing and is not necessarily synonymous with the direction it is moving in. This doesn’t actually matter for the wormhole particles, as they are perfectly round circles, so whichever way you rotate them they look the same (also known as circular symmetry), but I’ve put relative out of habit. If the particle were a line, the line would always point towards the direction it is moving with the last argument set to 1.


part_type_blend(wormhole_part,1);

Arguments

  • Whether blending is set to additive or not.

This function is key to making that cool “glowy” effect, combined with using the right sprite for the particle (that’s coming below). By setting blending to additive, multiple particles that overlap with light colours get blended together, eventually ending up as pure white with enough particles overlapping. That’s what makes the ring light up like it does. Setting it to not additive simply makes particles overlap like normal sprites would (you can achieve the same effect with ordinary sprites by using the gpu_set_blendmode() function).


part_type_alpha3(wormhole_part,1,1,0);

Arguments

  • Alpha value for the particle when it is created.
  • Alpha value the particle should reach by halfway through it’s lifespan.
  • Alpha value the particle should reach by the end of it’s lifespan.

This just makes the particles fade in and out as they are created and die. There are two variations to this function: part_type_alpha1() and part_type_alpha2(). They both do what they kinda sound like they do: Alpha1 takes only 1 argument beyond the particle name, and that is the alpha the particle will be at for it’s entire lifespan. Alpha2 is the same, except it takes 2 arguments, the start alpha and end alpha. But since we want the start, middle and end alpha to be different, we use alpha3.


part_type_color2(wormhole_part,make_color_rgb(255,0,102),make_color_rgb(0,204,255));

Arguments

  • The colour the particle should be when it is created.
  • The colour the particle should change to by the time it dies.

These functions are roughly the same as the alpha functions, there’s three variations and they function the same as the alpha functions, except related to colour obviously. We make use of the make_color_rgb() functions to provide our own red, green and blue values to make specific colours. The first is a reddish purple and the second is a light blue.


part_type_sprite(wormhole_part,part_wormhole_glow,0,0,0);

Arguments

  • The sprite you want the particle to use.
  • Whether you want the particle to be animated or not (requires the sprite to have multiple frames).
  • Whether to stretch the animation to last for the entire length of the particles lifespan, or whether to simply loop the animation while the particle lasts.
  • Should the particle pick a random frame from the sprite to start on.

Ok, so this is the final function, and makes the particle use a custom sprite. This one to be precise:

Just right click to download the image, add it into your game as a sprite and name the sprite part_wormhole_glow. It’s not animated and there’s no extra frames, so all the other arguments can be 0.


part 3: Warping Spacetime

Ok, wow, that was a lot of explanation. But as you can see, particles are quite malleable and very useful for creating some really nice effects without the cost in processing that would come with using instances to do the same thing.

So now we’ve got our wormhole particles setup, all we need to do is run our project again and bam:

Now that looks pretty sweet. I’ve made it follow the mouse, but of course, you can just have it centered on the instance by changing the code in the Step Event to:

part_particles_create(wormhole_ps_upper, x, y, wormhole_part, 10);

part 4: Connecting The Dots

Now we’ve got our wormhole particles, lets create the particles for the line connecting them. I’m not going to go as in-depth with this section, but we do get to use the emitter we created in PART 1: MAKING YOUR FIRST PARTICLE. Firstly, we need to create two new particle types, wormhole_line_part and wormhole_connect_part. Open up the Create Event again and put this below everything we’ve done so far:

// ** Particle : wormhole_line_part **
wormhole_line_part = part_type_create();
part_type_size(wormhole_line_part,0,0.5,-0.01,0);
part_type_scale(wormhole_line_part,1,1);
part_type_life(wormhole_line_part,60,120);
part_type_gravity(wormhole_line_part,0,0);
part_type_speed(wormhole_line_part,1,2,0,0);
part_type_direction(wormhole_line_part,0,0,0,0);
part_type_orientation(wormhole_line_part,0,0,0,0,1);
part_type_blend(wormhole_line_part,1);
part_type_alpha3(wormhole_line_part,0,1,0);
part_type_color2(wormhole_line_part,make_color_rgb(0,255,204),make_color_rgb(0,102,255));
part_type_sprite(wormhole_line_part,part_wormhole_line,0,0,0);

// ** Particle : wormhole_connect_part **
wormhole_connect_part = part_type_create();
part_type_size(wormhole_connect_part,0,0.5,-0.01,0);
part_type_scale(wormhole_connect_part,1,1);
part_type_life(wormhole_connect_part,120,200);
part_type_gravity(wormhole_connect_part,0,0);
part_type_speed(wormhole_connect_part,0.25,0.5,0,0);
part_type_direction(wormhole_connect_part,0,0,0,0);
part_type_orientation(wormhole_connect_part,0,0,0,0,1);
part_type_blend(wormhole_connect_part,1);
part_type_alpha3(wormhole_connect_part,0,1,0);
part_type_color2(wormhole_connect_part,make_color_rgb(102,0,255),make_color_rgb(204,0,255));
part_type_sprite(wormhole_connect_part,part_wormhole_connect,0,0,0);

As you can see, we use the same basic arguments as for the original particle, but tweak them a little bit. The biggest change is probably that the relative orientation argument is set to true, meaning that the particles will face the direction they are heading. We’ve also got two new sprites, so here they are:

This sprite should be added to the game and called part_wormhole_line.

And this sprite should be added and called part_wormhole_connect.

Now we’ll setup the emitter. But first, I need to add a little explanation, and you’ll need to stretch your own coding skills. The linking line requires the instance ID of another instance in the room…The one that it is linking too. It’s up to you to get that instance ID. I save which bodies are linked by saving the instance ID of the “other” wormhole into a variable called linked_body as I’m placing the wormholes when creating the maps, but you could use a variety of methods (hint: make sure that only one of the wormholes runs this code, otherwise you’ll have two lines going back and forth on top of one another, I set a primary variable to true in one of the linked wormholes and only run this code in the wormhole that has primary set to true…Of course, if you don’t care about two lines on top of one another feel free to ignore this).

Now that you’ve figured out a method of saving the instance ID into the linked_body variable, open the Step Event and place this code into it:

if (instance_exists(linked_body)) {
	var xmin = x;
	var xmax = linked_body.x;
	var ymin = y;
	var ymax = linked_body.y;
	var _dir = point_direction(x,y,linked_body.x,linked_body.y);
	part_type_direction(wormhole_line_part,_dir,_dir,0,0);
	part_type_direction(wormhole_connect_part,_dir,_dir,0,0);
	part_emitter_region(wormhole_ps_upper,wormhole_connect_emit,xmin,xmax,ymin,ymax,3,1);
	part_emitter_burst(wormhole_ps_upper,wormhole_connect_emit,wormhole_line_part,7);
	part_emitter_burst(wormhole_ps_upper,wormhole_connect_emit,wormhole_connect_part,4);
}

Now, once again, that might seem a little complicated, but each individual step is simple. Firstly, we want to store the x and y of the wormhole and it’s linked body for ease of use.

Then we figure out the direction the particles need to move (and because their relative orientation is set to true, this also makes them face the correct direction) using the point_direction() function. We then set the direction of the particles using the part_type_direction() function (this would be more efficient if done in the Create Event, but I didn’t feel like explaining the linked_body stuff until now, so here’s where we’ll do it, it’s not that big of a deal).

Penultimately, we set the emitter region. An emitter region is very simply the region in which particles will be created. They can spawn randomly at any point within the region that is specified. Let’s go a little deeper:


part_emitter_region(wormhole_ps_upper,wormhole_connect_emit,xmin,xmax,ymin,ymax,3,1);

Arguments

  • The particle system we want the region to be associated with.
  • The name of the emitter we want the region to be associated with.
  • The minimum x value of the region.
  • The maximum x value of the region.
  • The minimum y value of the region.
  • The maximum y value of the region.
  • The shape of the region.
  • The distribution within the region that particles can spawn in.

“But hey!” I hear you saying, “If you have a minimum x and y value and a maximum x and y value, surely that creates a box? How do we make the particles spawn in a line?”

The answer lies within the 7th argument, or the shape of the region. There’s 4 basic shapes you can make your region into. 0 is a square, 1 is a circle, 2 is a diamond and 3…Well 3 is a line. So we input 3 into the 7th argument and the particles will spawn in a line with the line starting at xmin and ymin and finishing at xmax and ymax. We also want the 8th argument, or the distribution to be set to 1. The distribution is basically where within the region can particles spawn. 0 is the full region, 1 is the “center” (basically the inverse of the border), and 2 is only along the border of the region.

So now we have the region setup, we just want to “burst” particles along it every step (I prefer bursting rather than streaming as you can lock the burst inside a flag, if need be, and it’ll stop creating particles whenever you “not” the flag. Both the burst functions are pretty similar, so I’ll just go over one of them:


part_emitter_burst(wormhole_ps_upper,wormhole_connect_emit,wormhole_line_part,7);

Arguments

  • The particle system you want the burst to be associated with.
  • The emitter you want the burst to come from.
  • The particle you want the burst to create.
  • The number of particles to spawn.

Pretty simple really. The last thing you always need to remember to do is clean up after yourself. Particle systems, emitters and particles need to be destroyed, and if you’re not creating a global particle system and particles (which we are not), then you need to destroy them when the instance is removed/destroyed. So open up the Cleanup Event and put the following code in:

part_emitter_destroy_all(wormhole_ps_upper);

part_type_destroy(wormhole_part);
part_type_destroy(wormhole_line_part);
part_type_destroy(wormhole_connect_part);

part_system_destroy(wormhole_ps_upper);

The code is pretty self-explanatory. Destroy all the emitters in the particle system wormhole_ps_upper, destroy the particles then destroy the system itself. If you don’t do this, you’ll end up with a memory leak, so make sure you do. So that’s it for creating the wormhole effect, but let’s have a little chat about experimenting.

part 5: The Age Of Experimentation

Playing around with particle values is essential to learning how to make nice effects. Don’t be afraid to open a blank project and try out inputting different values into the part_type_*() functions and seeing what happens. There’s also a ton of different “particle creator” assets that can be found on the yoyogames marketplace which can really help speed up experimentation (however, I really do think it’s vital to understand the actual code because there’s nothing more powerful than the ol’ noggin).

Here’s a few tricks that I’ve learnt.


You can set the part_type_*() functions immediately before spawning a single particle, change the part_type_*() function after that and create another particle. This lets you do things like a “sucking in to a point” effect, which seems impossible given a naive view of the particle settings:

if (mouse_check_button_released(mb_left)) {
	repeat(10) {
		var _dir = random(360);
		var _len = 150;
		var spawn_x = mouse_x+lengthdir_x(_len,_dir); // The x and y where the particle will spawn
		var spawn_y = mouse_y+lengthdir_y(_len,_dir);
		var _part_dir = point_direction(spawn_x,spawn_y,mouse_x,mouse_y); // The direction from the particle spawn to the "center point" or the mouse in this case
		part_type_direction(wormhole_part,_part_dir,_part_dir,0,0);
		part_particles_create(wormhole_ps_upper,spawn_x,spawn_y,wormhole_part,1);
	}
}

Just tweak a few settings in the Create Event like the lifespan, the speed the particles move at, etc, and viola!


Never be afraid to create your own particle sprites. This is probably one of the biggest things that gets neglected, but by crafting sprites exactly, you can end up with some pretty sweet effects:

// CREATE EVENT

// ** Particle : wormhole_part **
wormhole_part = part_type_create();
part_type_size(	wormhole_part,0.25,0.5,0,0.25);
part_type_scale(wormhole_part,1,1);
part_type_life(	wormhole_part,60,120);
part_type_gravity(wormhole_part,0,0);
part_type_speed(wormhole_part,1,3,0,0);
part_type_direction(wormhole_part,0,359,2,0);
part_type_orientation(wormhole_part,0,0,0,0,1);
part_type_blend(wormhole_part,1);
part_type_alpha3(wormhole_part,0,1,0);
part_type_color2(wormhole_part,make_color_rgb(204,255,0),make_color_rgb(0,255,204));
part_type_sprite(wormhole_part,part_inner_outer_glow,1,1,0);

// STEP EVENT

if (mouse_check_button_released(mb_left)) {
	part_particles_create(wormhole_ps_upper,mouse_x,mouse_y,wormhole_part,100);
}

Sprite:


Things don’t need to glow to look good either.

// CREATE EVENT

// ** Particle : wormhole_part **
wormhole_part = part_type_create();
part_type_size(	wormhole_part,0.05,0.1,0.0025,0);
part_type_scale(wormhole_part,1,1);
part_type_life(	wormhole_part,60,60);
part_type_gravity(wormhole_part,0.15,270);
part_type_speed(wormhole_part,1,4,-0.05,0);
part_type_direction(wormhole_part,140,90,0,0);
part_type_orientation(wormhole_part,0,360,0,0,0);
part_type_blend(wormhole_part,0);
part_type_alpha3(wormhole_part,1,1,0);
part_type_color2(wormhole_part,c_white,c_white);
part_type_sprite(wormhole_part,part_clouds,0,0,1);

// STEP EVENT

if (mouse_check_button_released(mb_left)) {
	part_particles_create(wormhole_ps_upper,mouse_x,mouse_y,wormhole_part,3);
}

Sprites:


You can change the origin point on your sprites to create some more cool stuff. Here’s one with origin at top-center:

// CREATE EVENT

// ** Particle : wormhole_part **
wormhole_part = part_type_create();
part_type_size(	wormhole_part,0.5,0.5,-0.01,0);
part_type_scale(wormhole_part,1,1);
part_type_life(	wormhole_part,60,60);
part_type_gravity(wormhole_part,0,0);
part_type_speed(wormhole_part,0,0,0,0);
part_type_direction(wormhole_part,0,359,0,0);
part_type_orientation(wormhole_part,0,360,10,0,0);
part_type_blend(wormhole_part,1);
part_type_alpha3(wormhole_part,0,1,0);
part_type_color2(wormhole_part,make_color_rgb(204,255,0),make_color_rgb(0,255,204));
part_type_sprite(wormhole_part,part_bar_glow_top_center,1,1,0);

// STEP EVENT

if (mouse_check_button_released(mb_left)) {
	for (var i=0;i<10;i++) {
		part_type_orientation(wormhole_part,i*36,i*36,5,0,0);
		part_particles_create(wormhole_ps_upper,mouse_x,mouse_y,wormhole_part,1);
	}
}

Sprite:


You can combine things unrelated to particles with particle generation in interesting ways:

// CREATE EVENT

// ** Particle : wormhole_part **
wormhole_part = part_type_create();
part_type_size(	wormhole_part,0.5,0.5,0,0);
part_type_scale(wormhole_part,1,1);
part_type_life(	wormhole_part,room_speed,room_speed);
part_type_gravity(wormhole_part,0,0);
part_type_orientation(wormhole_part,0,0,0,0,1);
part_type_blend(wormhole_part,0);
part_type_alpha2(wormhole_part,1,0);
part_type_color2(wormhole_part,c_aqua,c_green);
part_type_sprite(wormhole_part,part_bar_glow,0,0,0);

start_x = mouse_x;
start_y = mouse_y;
timer = 0;

// STEP EVENT

if (mouse_check_button_pressed(mb_left)) {
	start_x = mouse_x;
	start_y = mouse_y;
}
if (mouse_check_button(mb_left)) {
	timer--;
	if (timer < 0) {
		timer = room_speed/8;
		var _dir = point_direction(start_x,start_y,mouse_x,mouse_y);
		var _dist = point_distance(start_x,start_y,mouse_x,mouse_y);
		var _time = room_speed;
		part_type_direction(wormhole_part,_dir,_dir,0,0);
		part_type_speed(wormhole_part,_dist/_time,_dist/_time,0,0);
		part_particles_create(wormhole_ps_upper,start_x,start_y,wormhole_part,1);
	}
}

This is using the sprite for the previous effect.


part 6: Where Do We Go From Here?

Here we are, at the end. I had a lot of fun writing this tutorial and hopefully you’ve come out of it with some inspiration and knowledge. So go forth now, my friends, and make some crazy cool bursts of visual joy for your games. Oh, and if you don’t mind, go ahead and give my game a wishlist on Steam 😉

By RefresherTowel

I'm a solo indie game developer, based in Innisfail, Australia.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s