Tutorial 12: Particles
In this tutorial you will learn how to use particles scene node.
Before starting this tutorial be sure to setup an empty project with the Setup Tutorial.
what are particles?
"Particles" in computer graphics usually refers to a group of many small entities that compose a single effect. for example, a fire made of small 'flame' entities, blood splatter composed of multiple animated blood drops, and even rain effects are sometimes made with particles system.
In this tutorial we will see how to use Ness-Engine built-in particles engine to create the following flame effect:
That's it. :)
Now that you know about particles, it's time to learn about static scene nodes!
continue to next tutorial -->
Our ParticlesEmitter class inherit from Ness-Engine ParticlesEmitter API, and implement only one basic function: emit_particle(). this function will create and return a single particle instance, and will be called by the ParticlesNode every time it needs to generate a new particle.
Now let's view the body of this function. first, we create a new Particle instance:
// Emit flame particles
class ParticlesEmitter : public Ness::ParticlesEmitter
{
virtual Ness::ParticlePtr emit_particle(Ness::Renderer* renderer)
{
// create the particle to return
Ness::ParticlePtr ret = ness_make_ptr<Ness::Particle>(renderer, "../ness-engine/resources/gfx/flame.png");
// set random position and size, anchor to center
ret->set_position(Ness::Point(-50.0f + rand() % 100, -50.0f + rand() % 100));
ret->set_scale(0.5f + ((float)(rand() % 10) / 10.0f));
ret->set_anchor(Ness::Point::HALF);
ret->set_blend_mode(Ness::BLEND_MODE_ADD);
// add animators to particle
ret->register_animator(ness_make_ptr<Ness::Animators::AnimatorFaderOut>(ret, true, 2.0f, 0.35f));
ret->register_animator(ness_make_ptr<Ness::Animators::AnimatorFaderIn>(ret, 4.5f));
ret->register_animator(ness_make_ptr<Ness::Animators::AnimatorScaler>(ret, Ness::Point(1.0f, 1.0f)));
return ret;
}
};


[on the left is the basic flame effect, on the right is two examples of how it is used in a game made with ness-engine]

Particles node, Particles and Emitter
The particles system in Ness-Engine is made from three main objects:
-
Particles - which are the particles composing the effect (i.e. the flames).
-
ParticlesNode - which is the scene node that hold and manage the particles.
-
ParticlesEmitter - an object that generates the particles ("emits" them) for the ParticlesNode.
Creating the Emitter
Let's begin by creating our ParticlesEmitter class:
// create the particle to return
Ness::ParticlePtr ret = ness_make_ptr<Ness::Particle>(renderer, "../ness-engine/resources/gfx/flame.png");
Note how we don't add this particle to any scene node or AnimatorsQueue. we just create it as an individual object.
next, we'll set its starting position and other properties:
// set random position and size, anchor to center
ret->set_position(Ness::Point(-50.0f + rand() % 100, -50.0f + rand() % 100));
ret->set_scale(0.5f + ((float)(rand() % 10) / 10.0f));
ret->set_anchor(Ness::Point::HALF);
ret->set_blend_mode(Ness::BLEND_MODE_ADD);
The position we set is not absolute; it is relative to the position of the ParticlesNode at the time we created this particle.
Next we add some animators to this particle and return it:
// add animators to particle
ret->register_animator(ness_make_ptr<Ness::Animators::AnimatorFaderOut>(ret, true, 2.0f, 0.35f));
ret->register_animator(ness_make_ptr<Ness::Animators::AnimatorFaderIn>(ret, 4.5f));
ret->register_animator(ness_make_ptr<Ness::Animators::AnimatorScaler>(ret, Ness::Point(1.0f, 1.0f)));
return ret;
Note how we register the animators directory on the particle itself. this is possible because the particle is also an AnimatedSprite entity. if you find yourself confused by the concepts of Animators and AnimatedSprite, please review this tutorial.
Another important thing to notice is the fact that the first animator we add, the AnimatorFaderOut, will also remove this particle once done. it's absolutly crucial to add at least one animator that will eventually remove the particle from the system, otherwise the particles node will just keep on generating new particles without them clearing out (which might also be ok to some effects, as long as you remember to make it stop at some point).
you can now create an instance of the emitter class and test it on your own - just create some particles and add them to a scene node to see how the individual particles looks like.
Creating the ParticlesNode
Now it's time to create the particles node and provide it with the emitter:
// create the particles node
Ness::ParticlesNodePtr particles = scene->create_particles_node(Ness::Size(150, 150));
// set particles emit settings
Ness::SParticlesNodeEmitSettings EmitSettings;
EmitSettings.particles_emitter = ness_make_ptr<ParticlesEmitter>();
EmitSettings.emitting_interval = 0.045f;
EmitSettings.max_particles_count = 100;
EmitSettings.max_particles_emit = 3;
EmitSettings.min_particles_emit = 1;
EmitSettings.chance_to_emit = 85;
particles->set_emit_settings(EmitSettings);
First we create the particles node just as we create any other scene node or entity. Note the size we provide: Ness::Size(150, 150). the size parameter is the estimated size of the entire particles system, and its used for optimizations, like not generating particles when the system is out of screen.
you can configure the particles system to always generate particles, even when out of screen, by calling particles->set_emit_when_not_visible(true);
Next thing we do is creating EmitSettings struct. this object tells the particles system how to behave and generate the particles:
-
particles_emitter - a pointer to an instance of our ParticlesEmitter class we defined in previously. different particle nodes may have different particle emitters, in order to produce different particles.
-
emitting_interval - is intervals in seconds of how often to generate "emitting event". an emitting event is an event in which a new particle(s) may be generated (depends on other factors we'll see shortly).
-
max_particles_count - how many particles there can be simultaneously in this system. if the quota is exceeded no new particles will be generated until some particles are removed.
-
max_particles_emit - how many particles at most it can generate in the "emitting event" (meanning every 0.045f seconds it can generate up to 3 particles)
-
min_particles_emit - how many particles is must at least generate in the "emitting event". every time it generate particles it must create at least 1 particle.
-
chance_to_emit - is the chance in percents to even generate particles at the "emitting event". this means that only 85% of the emitting events will actually generate any particles.
So lets recap the params above and how this particles system will behave: every 0.045f seconds it will have 85% chance to generate 1 to 3 new particles, with a limit of maximum 100 particles existing simultaneously. to generate particles it will call the function in our customized particles emitter.
I urge you to try and play with the params above, and see how it affects the particles system.
also, there are additional params you can config to the EmitSettings. try to check them out.
Moving with node
If you tried moving the particles node while it had existing particles, you just witnessed a magic: while the new particles are generated at the new node position, the old existing particles do not move with the particles system!
This is on purpose and it is done by a special implementation of the particle. this is because in most cases when moving the particles system you only want to move the place where you emit new particles, and not the position of existing particles.
To change this behavior, you can call the function set_move_with_node(true) of the individual particles.
To understand better the difference try to add a code that moves the particles node with the position of your mouse, and then run it once with set_move_with_node(true) and once without it.
You can use the example code provided at the end of this tutorial.
Pause particles node
There are actually two ways to pause particles node:
pause_emitting(true): this will stop emitting new particles from the particles node until unpaused.
pause_animation(true): this will stop emitting new particles, but will also pause the existing particles, meaning the entire particles system will "freeze".
View live example
You can see what we've learned in this tutorial live in this example code.