namespace Sandbox; /// /// Ticks the physics in FrameStage.PhysicsStep /// [Expose] sealed class ParticleGameSystem : GameObjectSystem { private List workList = new( 64 ); public ParticleGameSystem( Scene scene ) : base( scene ) { Listen( Stage.FinishUpdate, 0, UpdateParticles, "UpdateParticles" ); } void UpdateParticles() { using var __ = PerformanceStats.Timings.Particles.Scope(); var particles = Scene.GetAll(); if ( particles.Count() == 0 ) return; var timeDelta = MathX.Clamp( Time.Delta, 0.0f, 1.0f / 30.0f ); var realTimeDelta = MathX.Clamp( RealTime.Delta, 0.0f, 1.0f / 30.0f ); workList.Clear(); foreach ( var p in particles ) { var delta = p.Timing switch { ParticleEffect.TimingMode.GameTime => timeDelta, ParticleEffect.TimingMode.RealTime => realTimeDelta, _ => timeDelta // default to GameTime }; p.TryPreWarm(); p.PreStep( delta ); p.CollectWork( workList ); } if ( workList.Count > 0 ) { System.Threading.Tasks.Parallel.ForEach( workList, ProcessWork ); } foreach ( var p in particles ) { p.SpawnDeferredParticleCollisionPrefabs(); p.PostStep(); } workList.Clear(); } /// /// We process the particles in chunks, in parallel. We don't do one particle at a time because /// it'd spend more time doing all the admin of giving them to threads than it would actually take. /// /// private void ProcessWork( ParticleEffect.ParticleWork work ) { for ( int i = work.startIndex; i < work.endIndex; i++ ) { work.effect.UpdateParticle( i ); } } }