diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp
index f0cf76ce4..8dae724ab 100644
--- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp
+++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp
@@ -505,7 +505,8 @@ void ParticleEmitterGraphCPUExecutor::Update(ParticleEmitter* emitter, ParticleE
}
// Spawn particles
- int32 spawnCount = 0;
+ int32 spawnCount = data.CustomSpawnCount;
+ data.CustomSpawnCount = 0;
if (canSpawn)
{
PROFILE_CPU_NAMED("Spawn");
@@ -573,7 +574,8 @@ int32 ParticleEmitterGraphCPUExecutor::UpdateSpawn(ParticleEmitter* emitter, Par
Init(emitter, effect, data, dt);
// Spawn particles
- int32 spawnCount = 0;
+ int32 spawnCount = data.CustomSpawnCount;
+ data.CustomSpawnCount = 0;
for (int32 i = 0; i < _graph.SpawnModules.Count(); i++)
{
spawnCount += ProcessSpawnModule(i);
diff --git a/Source/Engine/Particles/ParticleEffect.cpp b/Source/Engine/Particles/ParticleEffect.cpp
index ab3066372..9b033641c 100644
--- a/Source/Engine/Particles/ParticleEffect.cpp
+++ b/Source/Engine/Particles/ParticleEffect.cpp
@@ -281,6 +281,34 @@ void ParticleEffect::UpdateSimulation(bool singleFrame)
Particles::UpdateEffect(this);
}
+void ParticleEffect::SpawnParticles(int32 count, const StringView& emitterTrackName)
+{
+ auto system = ParticleSystem.Get();
+ if (!system)
+ return;
+ if (emitterTrackName.IsEmpty())
+ {
+ for (auto& e : Instance.Emitters)
+ e.CustomSpawnCount += count;
+ }
+ else
+ {
+ for (int32 i = 0; i < system->Tracks.Count(); i++)
+ {
+ auto& track = system->Tracks[i];
+ if (track.Type == ParticleSystem::Track::Types::Emitter && track.Name == emitterTrackName)
+ {
+ const int32 emitterIndex = track.AsEmitter.Index;
+ if (Instance.Emitters.IsValidIndex(emitterIndex))
+ {
+ Instance.Emitters.Get()[emitterIndex].CustomSpawnCount += count;
+ break;
+ }
+ }
+ }
+ }
+}
+
void ParticleEffect::Play()
{
_isPlaying = true;
diff --git a/Source/Engine/Particles/ParticleEffect.h b/Source/Engine/Particles/ParticleEffect.h
index 5e2770f89..2b32b3ca2 100644
--- a/Source/Engine/Particles/ParticleEffect.h
+++ b/Source/Engine/Particles/ParticleEffect.h
@@ -350,6 +350,13 @@ public:
/// True if update animation by a single frame only (time time since last engine update), otherwise will update simulation with delta time since last update.
API_FUNCTION() void UpdateSimulation(bool singleFrame = false);
+ ///
+ /// Manually spawns additional particles into the simulation.
+ ///
+ /// Amount of particles to spawn.
+ /// Name of the emitter track to spawn particles in. Empty if spawn particles into all tracks.
+ API_FUNCTION() void SpawnParticles(int32 count, const StringView& emitterTrackName = String::Empty);
+
///
/// Plays the simulation.
///
diff --git a/Source/Engine/Particles/ParticlesSimulation.cpp b/Source/Engine/Particles/ParticlesSimulation.cpp
index 880f6389d..3fafc8ad6 100644
--- a/Source/Engine/Particles/ParticlesSimulation.cpp
+++ b/Source/Engine/Particles/ParticlesSimulation.cpp
@@ -26,6 +26,7 @@ void ParticleEmitterInstance::ClearState()
Time = 0;
SpawnModulesData.Clear();
CustomData.Clear();
+ CustomSpawnCount = 0;
#if COMPILE_WITH_GPU_PARTICLES
GPU.DeltaTime = 0.0f;
GPU.SpawnCount = 0;
diff --git a/Source/Engine/Particles/ParticlesSimulation.h b/Source/Engine/Particles/ParticlesSimulation.h
index 19303b971..ec2bd5e05 100644
--- a/Source/Engine/Particles/ParticlesSimulation.h
+++ b/Source/Engine/Particles/ParticlesSimulation.h
@@ -84,6 +84,11 @@ public:
///
Array CustomData;
+ ///
+ /// The external amount of the particles to spawn.
+ ///
+ int32 CustomSpawnCount = 0;
+
struct
{
///