From 2906248f2f65efba396fa23ec725f8304a566cd9 Mon Sep 17 00:00:00 2001
From: Christopher Whitley <103014489+AristurtleDev@users.noreply.github.com>
Date: Tue, 9 Sep 2025 00:10:14 -0400
Subject: [PATCH] Use modifier frequency field (#1025)
---
.../Particles/Modifiers/AgeModifier.cs | 8 ++--
.../Containers/CircleContainerModifier.cs | 4 +-
.../Containers/RectangleContainerModifier.cs | 4 +-
.../RectangleLoopContainerModifier.cs | 4 +-
.../Particles/Modifiers/DragModifier.cs | 4 +-
.../Modifiers/LinearGravityModifier.cs | 4 +-
.../Particles/Modifiers/Modifier.cs | 43 ++++++++++++++++++-
.../Modifiers/ModifierExecutionStrategy.cs | 4 +-
.../Modifiers/OpacityFastFadeModifier.cs | 4 +-
.../Particles/Modifiers/RotationModifier.cs | 4 +-
.../Modifiers/VelocityColorModifier.cs | 4 +-
.../Particles/Modifiers/VelocityModifier.cs | 12 +++---
.../Particles/Modifiers/VortexModifier.cs | 4 +-
13 files changed, 71 insertions(+), 32 deletions(-)
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();