During replay playback, if we time jump to a point in time where a particle system should already be emitting its particles, some particles do not spawn. When tracing the offending particle emitters, they all shared the following characteristics:
- They all had a delay
- They had an infinite lifetime
- The max undilated frame time set in WorldSettings exceeded the particle emitter’s duration + delay
What essentially happens is that when time jumping, during a DemoNetDriver fast-forward, our game was taking near the max undilated frame time to recreate all the actors in the game, which would pass a relatively large delta time value on the first tick following that recreation. This was exasperated if we time dilated right after the time jump (for instance, a time dilation of 8 would 8x the max undilated frame time). However, the logic in the ParticleEmitterInstances class to spawn a particle is below:
if (!bHaltSpawning && !bHaltSpawningExternal && !bSuppressSpawning && (EmitterTime >= 0.0f)) { // If emitter is not done - spawn at current rate. // If EmitterLoops is 0, then we loop forever, so always spawn. if ((InCurrentLODLevel->RequiredModule->EmitterLoops == 0) || (LoopCount < InCurrentLODLevel->RequiredModule->EmitterLoops) || (SecondsSinceCreation < (EmitterDuration * InCurrentLODLevel->RequiredModule->EmitterLoops)) || bFirstTime) { bFirstTime = false; SpawnFraction = Spawn(DeltaTime); } }
What happens is:
- The first tick of that emitter had a large DeltaTime (let’s say 3 seconds if we 8x time dilate after we come out of a time jump)
- The emitter calculates how long it has been since the emitter has been created and if it has surpassed its expected duration
- In instances where the emitter duration is less than the delta time, the emitter think it already looped the particle effects and spawned it, resetting the delay period
- When we then check to see if we can spawn the particle, we don’t pass the first condition (EmitterTime > 0) because the emitter thinks we still need to wait for the delay
- If we do pass the first condition (because some DeltaTimes loop and do not completely refresh the delay period), we still don’t spawn the particle because SecondsSinceCreation !< EmitterDuration (SecondsSinceCreation += DeltaTime in its tick calculation)
- Before we even get to do the second tick, the particle system thinks the emitter has completed because no particles have spawned and the SecondsSinceCreation > EmitterDuration (in the HasCompleted() function).
- The particle system retires that emitter, effectively never spawning it in the first place, which is what we see in the replay playback.
Please let me know if I’m interpreting this logic incorrectly, but right now, it doesn’t seem like the ParticleEmitterInstance class accounts for large delta times in its spawning logic and skips spawning the particle altogether if the delta time passed in is larger than the duration + delay.