diff --git a/source/MonoGame.Extended/Particles/Modifiers/AgeModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/AgeModifier.cs index cf9c57f6..afc636a8 100644 --- a/source/MonoGame.Extended/Particles/Modifiers/AgeModifier.cs +++ b/source/MonoGame.Extended/Particles/Modifiers/AgeModifier.cs @@ -32,17 +32,17 @@ public class AgeModifier : Modifier /// Updates all particles by applying each interpolator in the collection to each particle. /// /// - public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator) + protected internal override unsafe void Update(float elapsedSeconds, ParticleIterator iterator, int particleCount) { if (!Enabled) { return; } - while (iterator.HasNext) + for (int i = 0; i < particleCount && iterator.HasNext; i++) { Particle* particle = iterator.Next(); - for (int i = 0; i < Interpolators.Count; i++) + for (int j = 0; j < Interpolators.Count; j++) { - Interpolators[i].Update(particle->Age, particle); + Interpolators[j].Update(particle->Age, particle); } } } diff --git a/source/MonoGame.Extended/Particles/Modifiers/Containers/CircleContainerModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/Containers/CircleContainerModifier.cs index e918685d..f4bab5ab 100644 --- a/source/MonoGame.Extended/Particles/Modifiers/Containers/CircleContainerModifier.cs +++ b/source/MonoGame.Extended/Particles/Modifiers/Containers/CircleContainerModifier.cs @@ -64,13 +64,13 @@ public class CircleContainerModifier : Modifier /// Updates all particles by constraining them to the circular boundary. /// /// - public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator) + protected internal override unsafe void Update(float elapsedSeconds, ParticleIterator iterator, int particleCount) { if (!Enabled) { return; } float radiusSq = Radius * Radius; - while (iterator.HasNext) + for (int i = 0; i < particleCount && iterator.HasNext; i++) { Particle* particle = iterator.Next(); diff --git a/source/MonoGame.Extended/Particles/Modifiers/Containers/RectangleContainerModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/Containers/RectangleContainerModifier.cs index b2281230..f73ab186 100644 --- a/source/MonoGame.Extended/Particles/Modifiers/Containers/RectangleContainerModifier.cs +++ b/source/MonoGame.Extended/Particles/Modifiers/Containers/RectangleContainerModifier.cs @@ -51,11 +51,11 @@ public sealed class RectangleContainerModifier : Modifier /// Updates all particles by constraining them to the rectangular boundary. /// /// - public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator) + protected internal override unsafe void Update(float elapsedSeconds, ParticleIterator iterator, int particleCount) { if (!Enabled) { return; } - while (iterator.HasNext) + for (int i = 0; i < particleCount && iterator.HasNext; i++) { Particle* particle = iterator.Next(); diff --git a/source/MonoGame.Extended/Particles/Modifiers/Containers/RectangleLoopContainerModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/Containers/RectangleLoopContainerModifier.cs index b6065f8b..a59edb20 100644 --- a/source/MonoGame.Extended/Particles/Modifiers/Containers/RectangleLoopContainerModifier.cs +++ b/source/MonoGame.Extended/Particles/Modifiers/Containers/RectangleLoopContainerModifier.cs @@ -30,11 +30,11 @@ public class RectangleLoopContainerModifier : Modifier /// Updates all particles by wrapping them around to the opposite side when they cross the rectangular boundary. /// /// - public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator) + protected internal override unsafe void Update(float elapsedSeconds, ParticleIterator iterator, int particleCount) { if (!Enabled) { return; } - while (iterator.HasNext) + for (int i = 0; i < particleCount && iterator.HasNext; i++) { Particle* particle = iterator.Next(); diff --git a/source/MonoGame.Extended/Particles/Modifiers/DragModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/DragModifier.cs index 627b63a8..aa495dd2 100644 --- a/source/MonoGame.Extended/Particles/Modifiers/DragModifier.cs +++ b/source/MonoGame.Extended/Particles/Modifiers/DragModifier.cs @@ -49,11 +49,11 @@ public class DragModifier : Modifier /// Updates all particles by applying drag forces based on their velocity. /// /// - public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator) + protected internal override unsafe void Update(float elapsedSeconds, ParticleIterator iterator, int particleCount) { if (!Enabled) { return; } - while (iterator.HasNext) + for (int i = 0; i < particleCount && iterator.HasNext; i++) { Particle* particle = iterator.Next(); diff --git a/source/MonoGame.Extended/Particles/Modifiers/LinearGravityModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/LinearGravityModifier.cs index e52eec1f..778b50e0 100644 --- a/source/MonoGame.Extended/Particles/Modifiers/LinearGravityModifier.cs +++ b/source/MonoGame.Extended/Particles/Modifiers/LinearGravityModifier.cs @@ -41,13 +41,13 @@ public class LinearGravityModifier : Modifier /// Updates all particles by applying a linear gravitational force. /// /// - public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator) + protected internal override unsafe void Update(float elapsedSeconds, ParticleIterator iterator, int particleCount) { if (!Enabled) { return; } Vector2 vector = Direction * (Strength * elapsedSeconds); - while (iterator.HasNext) + for (int i = 0; i < particleCount && iterator.HasNext; i++) { Particle* particle = iterator.Next(); diff --git a/source/MonoGame.Extended/Particles/Modifiers/Modifier.cs b/source/MonoGame.Extended/Particles/Modifiers/Modifier.cs index d4ada2cd..3a5a0ed1 100644 --- a/source/MonoGame.Extended/Particles/Modifiers/Modifier.cs +++ b/source/MonoGame.Extended/Particles/Modifiers/Modifier.cs @@ -2,6 +2,8 @@ // Licensed under the MIT license. // See LICENSE file in the project root for full license information. +using System; + namespace MonoGame.Extended.Particles.Modifiers; /// @@ -17,6 +19,10 @@ public abstract class Modifier { private const float DEFAULT_MODIFIER_FREQUENCY = 60.0f; + private float _frequency; + private float _cycleTime; + private int _particlesUpdatedThisCycle; + /// /// Gets or sets the display name of this modifier. /// @@ -40,7 +46,18 @@ public abstract class Modifier /// cost of performance. Lower values reduce CPU usage but may make particle changes appear /// less fluid. /// - public float Frequency; + public float Frequency + { + get => _frequency; + set + { + if (value <= 0.0f) + throw new ArgumentOutOfRangeException(nameof(value), "Frequency must be greater than zero."); + + _frequency = value; + _cycleTime = 1f / _frequency; + } + } /// /// Indicates whether this modifier is enabled. @@ -65,10 +82,32 @@ public abstract class Modifier Enabled = true; } + internal void InternalUpdate(float elapsedSeconds, ParticleIterator iterator) + { + if (!Enabled || iterator.Total == 0) + return; + + var particlesRemaining = iterator.Total - _particlesUpdatedThisCycle; + var particlesToUpdate = Math.Min(particlesRemaining, (int)Math.Ceiling((elapsedSeconds / _cycleTime) * iterator.Total)); + + if (particlesToUpdate > 0) + { + // Create a new iterator starting from the offset position + var offsetIterator = iterator.Reset(_particlesUpdatedThisCycle); + + Update(_cycleTime, offsetIterator, particlesToUpdate); + + _particlesUpdatedThisCycle += particlesToUpdate; + } + + if (_particlesUpdatedThisCycle >= iterator.Total) + _particlesUpdatedThisCycle = 0; + } + /// /// Updates the properties of particles according to this modifier's specific behavior. /// /// The elapsed time, in seconds, since the last update. /// The iterator used to iterate the particles ot update. - public abstract void Update(float elapsedSeconds, ParticleIterator iterator); + protected internal abstract void Update(float elapsedSeconds, ParticleIterator iterator, int particleCount); } diff --git a/source/MonoGame.Extended/Particles/Modifiers/ModifierExecutionStrategy.cs b/source/MonoGame.Extended/Particles/Modifiers/ModifierExecutionStrategy.cs index 35974e06..1786f393 100644 --- a/source/MonoGame.Extended/Particles/Modifiers/ModifierExecutionStrategy.cs +++ b/source/MonoGame.Extended/Particles/Modifiers/ModifierExecutionStrategy.cs @@ -59,7 +59,7 @@ public abstract class ModifierExecutionStrategy { for (int i = 0; i < modifiers.Count; i++) { - modifiers[i].Update(elapsedSeconds, iterator.Reset()); + modifiers[i].InternalUpdate(elapsedSeconds, iterator); } } @@ -80,7 +80,7 @@ public abstract class ModifierExecutionStrategy { internal override unsafe void ExecuteModifiers(List modifiers, float elapsedSeconds, ParticleIterator iterator) { - TPL.ForEach(modifiers, modifier => modifier.Update(elapsedSeconds, iterator.Reset())); + TPL.ForEach(modifiers, modifier => modifier.InternalUpdate(elapsedSeconds, iterator)); } public override string ToString() diff --git a/source/MonoGame.Extended/Particles/Modifiers/OpacityFastFadeModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/OpacityFastFadeModifier.cs index 5ed57d3b..fd4c4af2 100644 --- a/source/MonoGame.Extended/Particles/Modifiers/OpacityFastFadeModifier.cs +++ b/source/MonoGame.Extended/Particles/Modifiers/OpacityFastFadeModifier.cs @@ -32,11 +32,11 @@ public sealed class OpacityFastFadeModifier : Modifier /// Updates all particles by setting their opacity based on their age. /// /// - public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator) + protected internal override unsafe void Update(float elapsedSeconds, ParticleIterator iterator, int particleCount) { if (!Enabled) { return; } - while (iterator.HasNext) + for (int i = 0; i < particleCount && iterator.HasNext; i++) { Particle* particle = iterator.Next(); diff --git a/source/MonoGame.Extended/Particles/Modifiers/RotationModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/RotationModifier.cs index 21009992..4f648df3 100644 --- a/source/MonoGame.Extended/Particles/Modifiers/RotationModifier.cs +++ b/source/MonoGame.Extended/Particles/Modifiers/RotationModifier.cs @@ -32,13 +32,13 @@ public class RotationModifier : Modifier /// Updates all particles by applying rotation based on the elapsed time. /// /// - public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator) + protected internal override unsafe void Update(float elapsedSeconds, ParticleIterator iterator, int particleCount) { if (!Enabled) { return; } float rotationRateDelta = RotationRate * elapsedSeconds; - while (iterator.HasNext) + for (int i = 0; i < particleCount && iterator.HasNext; i++) { Particle* particle = iterator.Next(); diff --git a/source/MonoGame.Extended/Particles/Modifiers/VelocityColorModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/VelocityColorModifier.cs index dbef0545..c8e2d0c5 100644 --- a/source/MonoGame.Extended/Particles/Modifiers/VelocityColorModifier.cs +++ b/source/MonoGame.Extended/Particles/Modifiers/VelocityColorModifier.cs @@ -58,13 +58,13 @@ public class VelocityColorModifier : Modifier /// Updates all particles by changing their colors based on their current velocity. /// /// - public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator) + protected internal override unsafe void Update(float elapsedSeconds, ParticleIterator iterator, int particleCount) { if (!Enabled) { return; } float velocityThreshold2 = VelocityThreshold * VelocityThreshold; - while (iterator.HasNext) + for (int i = 0; i < particleCount && iterator.HasNext; i++) { Particle* particle = iterator.Next(); diff --git a/source/MonoGame.Extended/Particles/Modifiers/VelocityModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/VelocityModifier.cs index 3f230925..4a6091b2 100644 --- a/source/MonoGame.Extended/Particles/Modifiers/VelocityModifier.cs +++ b/source/MonoGame.Extended/Particles/Modifiers/VelocityModifier.cs @@ -38,13 +38,13 @@ public class VelocityModifier : Modifier /// Updates all particles by applying interpolators with an amount based on each particle's velocity. /// /// - public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator) + protected internal override unsafe void Update(float elapsedSeconds, ParticleIterator iterator, int particleCount) { if (!Enabled) { return; } float velocityThreshold2 = VelocityThreshold * VelocityThreshold; - while (iterator.HasNext) + for (int i = 0; i < particleCount && iterator.HasNext; i++) { Particle* particle = iterator.Next(); @@ -53,9 +53,9 @@ public class VelocityModifier : Modifier if (velocitySquared >= velocityThreshold2) { - for (int i = 0; i < Interpolators.Count; i++) + for (int j = 0; j < Interpolators.Count; j++) { - Interpolator interpolator = Interpolators[i]; + Interpolator interpolator = Interpolators[j]; interpolator.Update(1, particle); } } @@ -63,9 +63,9 @@ public class VelocityModifier : Modifier { float t = (float)Math.Sqrt(velocitySquared) / VelocityThreshold; - for (int i = 0; i < Interpolators.Count; i++) + for (int j = 0; j < Interpolators.Count; j++) { - Interpolator interpolator = Interpolators[i]; + Interpolator interpolator = Interpolators[j]; interpolator.Update(t, particle); } } diff --git a/source/MonoGame.Extended/Particles/Modifiers/VortexModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/VortexModifier.cs index 21fa1676..4e70ff63 100644 --- a/source/MonoGame.Extended/Particles/Modifiers/VortexModifier.cs +++ b/source/MonoGame.Extended/Particles/Modifiers/VortexModifier.cs @@ -52,11 +52,11 @@ public unsafe class VortexModifier : Modifier /// Updates all particles by applying a gravitational force towards the vortex center. /// /// - public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator) + protected internal override unsafe void Update(float elapsedSeconds, ParticleIterator iterator, int particleCount) { if (!Enabled) { return; } - while (iterator.HasNext) + for (int i = 0; i < particleCount && iterator.HasNext; i++) { Particle* particle = iterator.Next();