We are going to build a few different particle effects to demonstrate the various ways effects can be implemented in a game, and to look into some of the issues that might arise. To keep things straightforward, all of the effects we create will be a part of a single, global particle system. We will use both types of emitters, and utilize both shape and sprite-based particles. We will start with a Dust Cloud that will be seen anytime a Pillar is broken or destroyed. We will then add a system to create a unique shrapnel effect for each Pillar type. Finally, we will create some fire and smoke effects for the TNT explosion to demonstrate moving emitters.
The first effect we are going to create is a simple Dust Cloud. It will burst outwards upon the destruction of each Pillar and dissolve away over time. As this effect will be used in every level of the game, we will make all of its elements global, so they only need to be declared once.
scr_Global_Particles
.globalvar system; system = part_system_create();
globalvar dustEmitter; dustEmitter = part_emitter_create(system);
pt_shape_explosion
, which looks like a little thick cloud of dust. Add the following code to the end of the script:globalvar particle_Dust; particle_Dust = part_type_create(); part_type_shape(particle_Dust, pt_shape_explosion);
Once again we have made this a global variable, so that we have to create this Dust Cloud particle only once. We have declared only the shape attribute of this particle at this time. We will add more to this later once we can see what the effect looks like in the game.
scr_Global_GameStart
and call the particles script.scr_Global_Particles();
scr_Particles_DustCloud
, which we can use to set the region of the emitter and have it activate a burst of particles.part_emitter_region(system, dustEmitter, x-16, x+16, y-16, y+16, ps_shape_ellipse, ps_distr_gaussian); part_emitter_burst(system, dustEmitter, particle_Dust, 10);
We start by defining a small area for the emitter based on the position of instance that calls this script. The region itself will be circular with a Gaussian distribution so that the particles shoot out from the center. We then activate a single burst of 10 dust particles from the emitter.
scr_Pillar_Destroy
and insert the following line of code on the line before the instance is destroyed:scr_Particles_DustCloud();
scr_Pillar_BreakApart
and insert the same code in the same spot.scr_Global_Particles
and add the following code at the end of the script:part_type_life(particle_Dust, 15, 30); part_type_direction(particle_Dust, 0, 360, 0, 0); part_type_speed(particle_Dust, 1, 2, 0, 0); part_type_size(particle_Dust, 0.2, 0.5, 0.01, 0); part_type_alpha2(particle_Dust, 1, 0);
The first attribute we add is how long we want the particle to live for, which is a range between 15
and 30
steps, or at the speed of our rooms, a half to a whole second. Next, we want the particles to explode outwards, so we set the angle and add some velocity. Both functions that we are using have similar parameters. The first value is the particle type for which this is to be applied. The next two parameters are the minimum and maximum values from which a number will be randomly chosen. The fourth parameter sets an incremental value every step. Finally, the last parameter is a wiggle value that will randomly be applied throughout the particle's lifetime. For the Dust Cloud, we are setting the direction to be in any angle and the speed is fairly slow, ranging only a few pixels per step. We also want to change the size of the particles and their transparency, so that the dust appears to dissipate.
The Dust Cloud effect helps the Pillar destruction appear more believable, but it lacks the bigger chunks of material one would expect to see. We want some Shrapnel of various shapes and sizes to explode outwards for each of the different types of Pillars. We will start with the Glass particles.
spr_Particle_Glass
, and with Remove Background checked, load Chapter 8/Sprites/Particle_Glass.gif
. This sprite is not meant to be animated, though it does have several frames within it. Each frame represents a different shape of particle that will be randomly chosen when the particle is spawned.scr_Global_Particles
and initialize the Glass particle at the end of the script.globalvar particle_Glass; particle_Glass = part_type_create(); part_type_sprite(particle_Glass, spr_Particle_Glass, false, false, true);
Once we have created the global variable and the particle, we set the particle type to be a Sprite. When assigning Sprites there are a few extra parameters beyond which resources should be used. The third and fourth parameters are for whether it should be animated, and if so, should the animation stretch for the duration of the particle's life. In our case we are not using animation, so it has been set to false
. The last parameter is for whether we want it to choose a random subimage of the Sprite, which is what we do want it to do.
part_type_life(particle_Glass, 10, 20); part_type_direction(particle_Glass, 0, 360, 0, 0); part_type_speed(particle_Glass, 4, 6, 0, 0); part_type_orientation(particle_Glass, 0, 360, 20, 4, false);
When compared with the Dust Cloud, this particle will have a shorter lifespan but will move at a much higher velocity. This will make this effect more intense while keeping the general area small. We have also added some rotational movement through part_type_orientation
. The particles can be set to any angle and will rotate 20 degrees per frame with a variance of up to four degrees. This will give us a nice variety in the spin of each particle. There is one additional parameter for orientation, which is whether the angle should be relative to its movement. We have set it to false
as we just want the particles to spin freely.
scr_Particles_DustCloud
and insert a burst emitter before the Dust Cloud is emitted, so that the Glass particles appear behind the other effect.part_emitter_burst(system, dustEmitter, particle_Glass, 8);
spr_Particle_Wood
and spr_Particle_Steel
with the supplied images in Chapter 8/Sprites/
in the same manner as we did for Glass.scr_Global_Particles
, add particles for both Wood and Steel with the same attributes as Glass.myParticle
, to each of the different Pillars to allow us to spawn the appropriate particle. Open scr_Pillar_Glass_Create
and add the following code at the end of the script:myParticle = particle_Glass;
scr_Particles_DustCloud
and change the variable particle_Glass
to myParticle
as in the following code:part_emitter_burst(system, dustEmitter, myParticle, 8);
When the TNT explodes, it shoots out some TNT Fragments which are currently bland looking Sprites. We want the Fragments to be on fire as they streak across the scene. We also want a cloud of smoke to rise up from the explosion to indicate that the explosion we see is actually on fire. This is going to cause some complications. In order to make something appear to be on fire, it will need to change color, say from white to yellow to orange. As we have already mentioned, due to the fact that WebGL is not supported by all browsers, we cannot utilize any of the functions that allow us to blend colors together. This means that we need to work around this issue. The solution is to use several particles instead of one.
scr_Global_Colors
and add the following colors:orange = make_color_rgb(255, 72, 12); fireWhite = make_color_rgb(255, 252, 206); smokeBlack = make_color_rgb(24, 6, 0);
We already have a nice yellow color, so we add an orange, a slightly yellow tinted white, and a partially orange black color.
scr_Global_Particles
add a new particle for the smoke with the following attributes:globalvar particle_Smoke; particle_Smoke = part_type_create(); part_type_shape(particle_Smoke, pt_shape_smoke); part_type_life(particle_Smoke, 30, 50); part_type_direction(particle_Smoke, 80, 100, 0, 0); part_type_speed(particle_Smoke, 2, 4, 0, 0); part_type_size(particle_Smoke, 0.6, 0.8, 0.05, 0); part_type_alpha2(particle_Smoke, 0.5, 0); part_type_color1(particle_Smoke, smokeBlack); part_type_gravity(particle_Smoke, 0.4, 90);
We start by adding the particle and using the built-in smoke shape. We want the smoke to linger for a while, so we set its life to range between a minimum of a second to almost two full seconds. We then set the direction and speed to be more or less upwards so that the smoke rises. Next, we set the size and have it grow over time. With the alpha values, we don't want the smoke to be completely opaque, so we set it to start at half transparent and fade away over time. Next, we are using part_type_color1
which allows us to tint the particle without affecting the performance very much. Finally, we apply some gravity to the particles so that any angled particles float slowly upwards.
globalvar particle_FireOrange; particle_FireOrange = part_type_create(); part_type_shape(particle_FireOrange, pt_shape_smoke); part_type_life(particle_FireOrange, 4, 6); part_type_direction(particle_FireOrange, 70, 110, 0, 0); part_type_speed(particle_FireOrange, 3, 5, 0, 0); part_type_size(particle_FireOrange, 0.5, 0.6, 0.01, 0); part_type_alpha2(particle_FireOrange, 0.75, 0.5); part_type_color1(particle_FireOrange, orange); part_type_gravity(particle_FireOrange, 0.2, 90); part_type_death(particle_FireOrange, 1, particle_Smoke);
Once again we set up the particle using the built-in smoke shape, this time with a much shorter lifespan. The general direction is still mainly upwards, though there is more spread than the smoke. These particles are slightly smaller, tinted orange and will be partially transparent for its entire life. We have added a little bit of upward gravity, as this particle is in between fire and smoke. Finally, we are using a function that will spawn a single particle of smoke upon the death of each orange particle.
globalvar particle_FireYellow; particle_FireYellow = part_type_create(); part_type_shape(particle_FireYellow, pt_shape_flare); part_type_life(particle_FireYellow, 6, 12); part_type_direction(particle_FireYellow, 0, 360, 0, 0); part_type_speed(particle_FireYellow, 4, 6, 0, 0); part_type_size(particle_FireYellow, 0.4, 0.6, 0.01, 0); part_type_color1(particle_FireYellow, yellow); part_type_death(particle_FireYellow, 1, particle_FireOrange);
globalvar particle_FireWhite; particle_FireWhite = part_type_create(); part_type_shape(particle_FireWhite, pt_shape_flare); part_type_life(particle_FireWhite, 2, 10); part_type_direction(particle_FireWhite, 0, 360, 0, 0); part_type_speed(particle_FireWhite, 6, 8, 0, 0); part_type_size(particle_FireWhite, 0.3, 0.5, 0.01, 0); part_type_color1(particle_FireWhite, fireWhite); part_type_death(particle_FireWhite, 1, particle_FireYellow);
scr_TNT_Fragment_Create
and add the following code at the end of the script:myEmitter = part_emitter_create(system); part_emitter_region(system, myEmitter, x-5, x+5, y-5, y+5, ps_shape_ellipse, ps_distr_linear); part_emitter_stream(system, myEmitter, particle_FireWhite, 5);
We create an emitter with a fairly small area for spawning with balanced distribution. At every step, the emitter will create five new Fire particles as long as the emitter exists.
scr_TNT_Fragment_Step
and add the following code:part_emitter_region(system, myEmitter, x-5, x+5, y-5, y+5, ps_shape_ellipse, ps_distr_linear);
obj_TNT_Fragment
and add a destroy
event with a new Script, scr_TNT_Fragment_Destroy
, which removes the emitter attached.part_emitter_destroy(system, myEmitter);
This function will remove the emitter from the system without removing any of the particles that had been spawned.
At this point, we have built a good variety of effects using various particles and emitters. The effects have added a lot of polish to the game, but there is a flaw with the particles. If the player decides to restart the room or go to the SHOP immediately after the explosion has occurred, the emitters will not be destroyed. This means that they will continue to spawn particles forever, and we will lose all references to those emitters. The game will end up looking like the following screenshot:
obj_TNT_Fragment
and add a Room End event and attach scr_TNT_Fragment_Destroy
to it.obj_Overlord
, add a Room End event and attach a new Script, scr_Overlord_RoomEnd
, with the following line of code:part_particles_clear(system);
This function will remove any particle that exists within the system, but will not remove the particle type from memory. It is important that we do not destroy the particle type, as we would not be able to use a particle again if its type no longer exists.
3.137.163.197