Particle Emitter Technical Guide
Creating new types of emitters requires a custom ParticleEmitterInstance
and ParticleModuleTypeData
. This guide explains the key aspects of each of these and walks through the creation of a new custom emitter type.
Particle Emitter Reference
The ParticleEmitterInstance
variables and functions, as well as the ParticleModuleTypeData
class, are explained in this section.
The ParticleEmitterInstance Struct
A ParticleEmitterInstance
is a single particle emitter that represents an instance of an effect in a ParticleSystemComponent
.
Member Variables
The ParticleEmitterInstance
struct contains the following public variables:
Variable | Overview |
---|---|
StaticType | This is the type identifier of the emitter instance. It is used to identify the emitter as well as provide safe casting functionality. |
SpriteTemplate | Pointer to the UParticleSpriteEmitter template that the instance is based on. In the case of custom emitter types, any specific data required is usually stored in the TypeDataModule which will be detailed later in this document. |
Component | Pointer to the UParticleSystemComponent that owns the emitter instance. |
CurrentLODLevelIndex | The index of the currently set LOD level. |
CurrentLODLevel | Pointer to the UParticleLODLevel that is currently active. |
TypeDataOffset | The offset to the TypeData payload in the particle data. Used to easily retrieve per-particle data that is specific to the type of emitter. |
SubUVDataOffset | The offset to SubUV-specific payload in the particle data. Only relevant when the subUV interpolation mode is not set to NONE . |
Location | An FVector giving the location of the emitter instance. |
KillOnDeactivate | If true , the emitter instance will be killed (deleted) when it is deactivated. |
bKillOnCompleted | If true , the emitter instance will be killed (deleted) when it completes its cycle. |
ParticleData | Pointer to the particle data array. |
ParticleIndices | Pointer to the particle index array. Used to provide a round-robin system of using particle data. |
ModuleOffsetMap | Maps module pointers to their offset into the particle payload data. |
InstanceData | Pointer to the per-instance data array. |
InstancePayloadSize | The size of the per-instance data array. |
ModuleInstanceOffsetMap | Maps module pointers to their offset into the per-instance data. |
PayloadOffset | The offset to the start of the particle payload data. |
ParticleSize | The total size of a particle in bytes. |
ParticleStride | The stride between particles in the ParticleData array (to allow for potentially aligning particle data). |
ActiveParticles | The number of particles that are currently active in the emitter. |
MaxActiveParticles | The maximum number of particles that can be held in the particle data array. |
SpawnFraction | The fraction of time left over from the last frame spawning. |
SecondsSinceCreation | The number of seconds that have passed since the instance was created. |
EmitterTime | The position of the time in a single loop of the emitter. |
OldLocation | The previous location of the emitter. |
ParticleBoundingBox | The bounding box for the emitter. |
BurstFired | An array of entries for tracking the firing of bursts. |
LoopCount | The number of loops completed by the instance. |
IsRenderDataDirty | Flag indicating if the render data is dirty. |
Module_AxisLock | The AxisLock module, if present. Held to avoid searching for it each Tick. |
EmitterDuration | The current duration of the instance. |
EmitterDurations | An array of the durations at each LOD level of the instance. |
TrianglesToRender | The number of triangles the emitter will render this frame. |
MaxVertexIndex | The max vertex index that will be accessed by the emitter when rendering. |
Member Functions
The ParticleEmitterInstance struct contains the following member functions which provide the opportunity to override the base functionality:
Function | Overview |
---|---|
| Sets the KillOnDeactivate flag to the given value. |
| Sets the bKillOnCompleted flag to the given value. |
| Initializes the parameters for the structure given the providedParticleEmitter template and parent ParticleSystemComponent . |
| Called to Initialize the instance. |
| Called to resize the particle data array to the given number ofMaxActiveParticles . |
| Tick the instance with the given time slice. If bSuppressSpawning is true , do not spawn any new particles. |
| Rewind the emitter instance. |
| Return the bounding box for the instance. |
| Update the bounding box for the instance. This is where the final positioning of the particles takes place for the frame as all updates are guaranteed to have occurred by now. |
| Retrieve the number of per-particle bytes that the emitter requires. |
| Get the pointer to the per-instance data allocated for the given module. |
| Calculate the stride of a single particle for this instance. |
| Reset the burst list information for the instance. |
| Get the current burst rate offset - artificially increases DeltaTime to generate the bursts. |
| Spawn particles for the instance given the current time slice (DeltaTime). Take the leftover from last from (OldLeftover) into account. |
| Handle any pre-spawning actions required for the particles in this instance. |
| Return true if the instance has completed its run. |
| Handle any post-spawning actions required for the particles in this instance. |
| Kill off any dead particle - simply remove them from the active array. |
| Called when the instance is removed from the scene. |
| Retrieve the particle at the given Index. |
| Get the dynamic data for rendering this instance this frame. |
| Get the allocated size of the emitter instance - for memory tracking. |
The ParticleModuleTypeDataBase class
The ParticleModuleTypeDataBase
class provides the mechanism for generating custom emitter instances when creating a ParticleSystemfor use in the engine. For example, the ParticleModuleTypeDataMesh
class will result in a FParticleMeshEmitterInstance
getting created for the ParticleSystem.
Member Functions
The ParticleModuleTypeDataBase
struct contains the following public functions which aid in generating custom emitters:
Function | Overview |
---|---|
| This is the key to overriding emitter instance creation in the UE4 particle system. The function is called when a TypeData module is found in a UParticleEmitter that is being instantiated. In this function, the desired FParticleEmitterInstance structure should be created and returned. |
| Called when the module is first created. This function allows for you to set default values that make sense. |
| Called during the PreSpawn function of the emitter instance, this function allows for TypeDataModule-specific setup of a newly spawned particle. |
| Called prior to any updating of the emitter instance, this function allows for handling any updates that need to occur prior to updating particles with each module contained in the emitter. |
| Called after the updating of the emitter instance, this function allows for handling any updates that need to occur after updating particles with each module contained in the emitter. |
| Allows for overriding the screen alignment flags found on the particle emitter. Currently only used by the mesh emitter. |
Example Particle Emitter
Writing a custom emitter instance is a two-step process. First, a TypeDataModule
needs to be created which will provide the specific data for your emitter instance, as well as generate it at the appropriate time. As an example, an emitter instance will be created that spins the emitter instance in addition to the rotation of the 'parent' particle system component.
TypeDataModule Declaration
The first step is to create the TypeDataModule
that will generate the new emitter instance type.
UCLASS(editinlinenew, collapsecategories, hidecategories=Object)
public class UParticleModuleTypeDataSpinner : public UParticleModuleTypeDataBase
{
/**
* The amount to spin the emitter instance (in complete rotations) at
* the given time.
*/
UPROPERTY(Category=Spinner)
rawdistributionvector Spin;
#if CPP
/**
* Create the custom ParticleEmitterInstance.
*
* @param InEmitterParent The UParticleEmitter that holds this TypeData module.
* @param InComponent The UParticleSystemComponent that 'owns' the emitter instance being created.
* @return FParticleEmitterInstance* The create emitter instance.
*/
virtual FParticleEmitterInstance* CreateInstance(UParticleEmitter* InEmitterParent, UParticleSystemComponent* InComponent);
#endif
}
TypeDataModule Implementation
The constructor for the TypeDataModule
creates a UDistributionVectorConstant
to assign to the Spin
variable.
UParticleModuleTypeDataSpinner::UParticleModuleTypeDataSpinner(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
UDistributionVectorConstant* DistributionSpin = ConstructorHelpers::CreateDefaultSubobject<UDistributionVectorConstant>(this, TEXT("DistributionSpin"));
DistributionSpin->Constant = FVector(0.0f, 0.0f, 0.0f);
Spin.Distribution = DistributionSpin;
}
The CreateInstance()
function is called by the ParticleSystemComponent
that will hold the emitter instance. This is where theTypeDataModule
can create any type of ParticleEmitterInstance
to be utilized by the system.
FParticleEmitterInstance* UParticleModuleTypeDataSpinner::CreateInstance(UParticleEmitter* InEmitterParent, UParticleSystemComponent* InComponent)
{
// Create our Spinner emitter instance.
FParticleSpinnerEmitterInstance* SpinnerInst = ::new FParticleSpinnerEmitterInstance();
if (SpinnerInst)
{
// Initialize the parameters for the emitter.
SpinnerInst->InitParameters(InEmitterParent, InComponent);
return SpinnerInst;
}
// We failed. Return NULL to let a default sprite emitter be generated, or assert.
return NULL;
}
In this example, an instance of the FParticleSpinnerEmitterInstance
will be generated. It is derived from theFParticleSpriteEmitterInstance
to leverage as much existing code as possible.
Particle Emitter Declaration
The FParticleSpinnerEmitterInstance
custom emitter instance structure is defined as follows:
struct FParticleSpinnerEmitterInstance : public FParticleSpriteEmitterInstance
{
/** Pointer to the spinner TypeDatModule. */
UParticleModuleTypeDataSpinner* SpinnerTypeData;
/** The rotation that was used during the Tick call. */
FVector CurrentRotation;
/** The rotation of the component. */
FRotator ComponentRotation;
/** Constructor */
FParticleSpinnerEmitterInstance();
virtual void InitParameters(UParticleEmitter* InTemplate, UParticleSystemComponent* InComponent, bool bClearResources = true);
virtual void Tick(float DeltaTime, bool bSuppressSpawning);
virtual void UpdateBoundingBox(float DeltaTime);
/**
* Adjust the component rotation to take the instance rotation into account.
*/
void AdjustComponentRotation();
/**
* Restore the component rotation.
*/
void RestoreComponentRotation();
};
The following member variables are contained in the FParticleSpinnerEmitterInstance
:
Variable | Overview |
---|---|
SpinnerTypeData | A pointer to the UParticleModuleTypeDataSpinner . It is held on to directly to avoid casting the TypeData module each time we need to access it. |
CurrentRotation | An FVector which tracks the current rotation of the emitter instance. Grabbed in Tick() /TickEditor() to update the rotation, and stored for use during the UpdateBoundingBox() function. |
Particle Emitter Implementation
The following member functions are implemented for our custom emitter instance:
Function | |||||
---|---|---|---|---|---|
| |||||
| |||||
| The Tick() function is responsible for spawning and updating the particles in the instance. First, it gets the current rotation from the SpinnerTypeData distribution using the EmitterTime. Since the LocalToWorld of the parent component can be used in the Spawn() /Update() functions of various modules, we need to ensure that the emitter instance rotation is taken into account. This is accomplished by saving off the Rotation of the component, and then adding the emitter instance amount to it. The component transform is then updated to include the new rotation. The emitter instance is then ticked like normal by calling the super Tick() function. The component Rotation is then restored. | ||||
| The UpdateBoundingBox() function has to be overridden in this case to ensure that the emitter instance rotation is taken into account when bUseLocalSpace is true . | ||||
| The AdjustComponentRotation() function will alter the components LocalToWorld to account for the emitter instance rotation. | ||||
| The RestoreComponentRotation() function will remove the emitter instance rotation from the components LocalToWorld. |
Results
The following screen shots demonstrate the Spinner emitter instance in action. The settings were as follows:
-
The Spin distribution was set to a constant curve with a point at (Time=0,X=0,Y=0,Z=0) and another point at (Time=1,X=0,Y=0,Z=1)leading to the instance spinning around the Z-axis at a variable rate over the life of the emitter.
-
An InitialVelocity module was used with a Constant Distribution set to (X=100,Y=100,Z=100) so that all particles would be emitted in a straight-line stream (discounting the spinning of the instance).
-
An InitialColor module was used with StartColor set to a constant curve with a point at (Time=0,R=1,G=0,B=0) and another at(Time=1,R=0,G=0,B=1) leading to the particles being emitted going from red to blue over the life of the emitter.