Create a Particle System
Audectra has a powerful built-in particle system, which can be used via the SDK. This guide will show you a few examples of how you can build your layers with particle effects.
Particle System
The particle system is the foundation where all particles, emitters and forces live within and interact with each other. You can imagine it as some sort of particle container with plug-in slots for various emitters and forces. It is, where all emitted particles are simulated throughout their individual lifetimes in interaction with different forces and rendered onto a given canvas.
Let's create an effect layer with a particle system, that we will use throughout this guide.
[EffectLayer(Name = "Particle Effect")]
public class ParticleEffectLayer : EffectLayerBase
{
private readonly IParticleSystem _system;
public ParticleEffectLayer(IExtensionApi api, int canvasWidth, int canvasHeight)
: base(api, canvasWidth, canvasHeight)
{
// Create particle system via extension API.
_system = api.CreateParticleSystem();
}
// Update particle system within the update method.
public override void Update(float dt) => _system.Update(dt);
// Render particle system within the render method.
public override void Render(IRender render) => _system.Render(render);
}
Our particle system is up and running, but there are no particles? Let's talk about emitters next.
Particle Emitters
Emitters are responsible for generating new particles within the particle system. Furthermore, they are also responsible for bringing in some variety among the particles. You can imagine, generating the same copy of a particle and sending it off with the exact same speed and direction all the time could result in a pretty boring particle system. Thus, emitters allow defining ranges for various particle properties, within which the properties of each generated particle will be randomly assigned from.
Currently, the SDK exposes two different kinds of emitters.
Continuous Emitter
Continuous emitters are the most classic particle emitters. They, as the name suggests, continuously generate new particles within defined particle property ranges. The emitted rate of such an emitter defines how many particles per second will be generated.
Let's assume, we want to add such an emitter to our initial node and let the emitted rate be controlled via a setting.
[EffectLayer(Name = "Particle Effect")]
public class ParticleEffectLayer : EffectLayerBase
{
private readonly IParticleSystem _system;
private readonly IContinuousParticleEmitter _emitter;
// Expose the emit rate as an setting to our particle effect layer.
[LayerSetting]
public float EmitRate
{
get => _emitter.EmitRate;
set => _emitter.EmitRate = value;
}
public ParticleEffectLayer(IExtensionApi api, int canvasWidth, int canvasHeight)
: base(api, canvasWidth, canvasHeight)
{
// Create particle system via extension API.
_system = api.CreateParticleSystem();
// Create a continuous particle emitter with an initial
// emit rate of 500 particles per second.
_emitter = api.CreateContinuousParticleEmitter(500);
// Configure the nominal properties of generated particles.
_emitter.Position = new Vector2(0, 0);
_emitter.Velocity = 24;
_emitter.Angle = -45;
_emitter.Size = 32;
_emitter.Life = 8;
_emitter.Color = SKColors.Aqua;
// Configure the property ranges for generated particles.
_emitter.VelocityRange = 16;
_emitter.AngleRange = 90;
_emitter.SizeRange = 16;
_emitter.LifeRange = 4;
// Add the emitter to the particle system.
_system.AddEmitter(_emitter);
}
public override void Update(float dt) => _system.Update(dt);
public override void Render(IRender render) => _system.Render(render);
}
That's it, now we have a particle effect that continuously generates particles with an emitted rate that we can define via an input to our node.
Info
All particle properties within the emitter can be changed during the lifetime of the emitter and will take effect in the next update cycle.
The particle property ranges create a symmetric span around the nominal value, whereas the span size is half the range.
For example, given that the nominal Size
of a particle is 48
and the property range SizeRange
is 32
, then the
particle size can vary between the values
[N - R / 2, N + R / 2]
[48 - 32/2, 48 + 32/2] = [48 - 16, 48 + 16] = [32, 64]
N ... nominal property value
R ... property range
Burst Emitter
In contrast to the continuous emitter, the burst emitter only emits a burst of particles when manually triggered.
[EffectLayer(Name = "Particle Effect")]
public class ParticleEffectLayer : EffectLayerBase
{
private readonly IParticleSystem _system;
private readonly IBurstParticleEmitter _emitter;
// Expose trigger as input to layer, such that we can trigger a burst externally.
[TriggerLayerSetting(nameof(OnTriggered))]
public bool Trigger { get; set; }
public ParticleEffectLayer(IExtensionApi api, int canvasWidth, int canvasHeight)
: base(api, canvasWidth, canvasHeight)
{
// Create particle system via extension API.
_system = api.CreateParticleSystem();
// Create a burst particle emitter.
_emitter = api.CreateBurstParticleEmitter();
// Configure the nominal properties of generated particles.
_emitter.Position = new Vector2(0, 0);
_emitter.Velocity = 24;
_emitter.Angle = -45;
_emitter.Size = 32;
_emitter.Life = 8;
_emitter.Color = SKColors.Aqua;
// Configure the property ranges for generated particles.
_emitter.VelocityRange = 16;
_emitter.AngleRange = 90;
_emitter.SizeRange = 16;
_emitter.LifeRange = 4;
// Add the emitter to the particle system.
_system.AddEmitter(_emitter);
}
public override void Update(float dt) => _system.Update(dt);
public override void Render(IRender render) => _system.Render(render);
private void OnTriggered(bool value)
{
// If the trigger has been activated, trigger a particle burst via the emitter.
if (value)
_emitter.Burst(500);
}
}
Info
When multiple bursts are triggered within one update cycle, only the last one will be processed.
The configuration of properties for generated particles is the same as for the previous emitter (see above).
Particle Forces
Now that we generate new particles within the particle system, let's talk about how to apply external forces to active particles. These particle forces allow us to manipulate the movement of particles during their lifetime after they have been generated.
Info
You can add up to 32 forces to a particle system.
Currently, the SDK exposes one particle force.
Directional Push Force
The directional force can be imagined as wind blowing the particles with configurable strength in a configurable direction.
Info
A positive strength value will push particles in the given direction. By passing a negative strength value, particles will be pulled towards this direction.
Our example particle effect layer currently emits particles from the top left corner. Let's add a directional push towards the right top corner. Let's also assume, we want to expose the force strength as input to our node.
[EffectLayer(Name = "Particle Effect")]
public class ParticleEffectLayer : EffectLayerBase
{
private readonly IParticleSystem _system;
private readonly IContinuousParticleEmitter _emitter;
private readonly IDirectionalPushParticleForce _force;
// Expose the force strength as setting to our layer.
[LayerSetting]
public float ForceStrength
{
get => _force.Strength;
set => _force.Strength = value;
}
public ParticleEffectLayer(IExtensionApi api, int canvasWidth, int canvasHeight)
: base(api, canvasWidth, canvasHeight)
{
// Create particle system via extension API.
_system = api.CreateParticleSystem();
// Create a continuous particle emitter with an initial
// emit rate of 500 particles per second.
_emitter = api.CreateContinuousParticleEmitter(500);
// Configure the nominal properties of generated particles.
_emitter.Position = new Vector2(0, 0);
_emitter.Velocity = 24;
_emitter.Angle = -45;
_emitter.Size = 32;
_emitter.Life = 8;
_emitter.Color = SKColors.Aqua;
// Configure the property ranges for generated particles.
_emitter.VelocityRange = 16;
_emitter.AngleRange = 90;
_emitter.SizeRange = 16;
_emitter.LifeRange = 4;
// Add the emitter to the particle system.
_system.AddEmitter(_emitter);
// Create a directional push force with initial values
// of 45 deg as direction angle and 16 as force strength.
_force = api.CreateDirectionalPushParticleForce(45, 16);
// Add the force to the particle system.
_system.AddForce(_force);
}
public override void Update(float dt) => _system.Update(dt);
public override void Render(IRender render) => _system.Render(render);
}
Info
Any changes to the force strength or direction will be considered within the next update cycle.