From 2ea7e8539834b9596edf6fe0a2a688afe042e9e9 Mon Sep 17 00:00:00 2001
From: Christopher Whitley <103014489+AristurtleDev@users.noreply.github.com>
Date: Fri, 25 Jul 2025 00:52:05 -0400
Subject: [PATCH] Updates to particle system for ember editor (#1001)
* Add enabled state for modifiers
* Add enabled state for interpolators
* Move AutoTrigger to effect not emitters
* Remove LineUniformProfile
* Set Enabled field true by default
* Don't render invisible emitters
* Remove unused member
* Document update method
* Add documentation
* Remove test now that LineUniformProfile was removed
* Fix unit tests
---
.../Particles/Modifiers/AgeModifier.cs | 2 +
.../Containers/CircleContainerModifier.cs | 2 +
.../Containers/RectangleContainerModifier.cs | 2 +
.../RectangleLoopContainerModifier.cs | 2 +
.../Particles/Modifiers/DragModifier.cs | 2 +
.../Interpolators/ColorInterpolator.cs | 2 +
.../Interpolators/HueInterpolator.cs | 2 +
.../Modifiers/Interpolators/Interpolator.cs | 10 ++
.../Interpolators/OpacityInterpolator.cs | 2 +
.../Interpolators/RotationInterpolator.cs | 2 +
.../Interpolators/ScaleInterpolator.cs | 2 +
.../Interpolators/VelocityInterpolator.cs | 2 +
.../Modifiers/LinearGravityModifier.cs | 2 +
.../Particles/Modifiers/Modifier.cs | 12 +-
.../Modifiers/OpacityFastFadeModifier.cs | 2 +
.../Particles/Modifiers/RotationModifier.cs | 2 +
.../Modifiers/VelocityColorModifier.cs | 2 +
.../Particles/Modifiers/VelocityModifier.cs | 2 +
.../Particles/Modifiers/VortexModifier.cs | 2 +
.../Particles/ParticleEffect.cs | 44 +++++-
.../Particles/ParticleEffectReader.cs | 16 +-
.../Particles/ParticleEffectWriter.cs | 11 +-
.../Particles/ParticleEmitter.cs | 37 +----
.../Particles/Profiles/LineUniformProfile.cs | 58 -------
.../Particles/Profiles/Profile.cs | 12 --
.../SpriteBatchExtensions.ParticleEfffect.cs | 24 ++-
.../Particles/ParticleEffectReaderTests.cs | 146 +++++++-----------
.../Particles/ParticleEffectWriterTests.cs | 144 +++++++----------
28 files changed, 242 insertions(+), 306 deletions(-)
delete mode 100644 source/MonoGame.Extended/Particles/Profiles/LineUniformProfile.cs
diff --git a/source/MonoGame.Extended/Particles/Modifiers/AgeModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/AgeModifier.cs
index 6f0fabe1..cf9c57f6 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/AgeModifier.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/AgeModifier.cs
@@ -34,6 +34,8 @@ public class AgeModifier : Modifier
///
public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator)
{
+ if (!Enabled) { return; }
+
while (iterator.HasNext)
{
Particle* particle = iterator.Next();
diff --git a/source/MonoGame.Extended/Particles/Modifiers/Containers/CircleContainerModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/Containers/CircleContainerModifier.cs
index 32327a5a..e918685d 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/Containers/CircleContainerModifier.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/Containers/CircleContainerModifier.cs
@@ -66,6 +66,8 @@ public class CircleContainerModifier : Modifier
///
public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator)
{
+ if (!Enabled) { return; }
+
float radiusSq = Radius * Radius;
while (iterator.HasNext)
diff --git a/source/MonoGame.Extended/Particles/Modifiers/Containers/RectangleContainerModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/Containers/RectangleContainerModifier.cs
index 9a6de728..b2281230 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/Containers/RectangleContainerModifier.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/Containers/RectangleContainerModifier.cs
@@ -53,6 +53,8 @@ public sealed class RectangleContainerModifier : Modifier
///
public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator)
{
+ if (!Enabled) { return; }
+
while (iterator.HasNext)
{
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 9f7efb6c..b6065f8b 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/Containers/RectangleLoopContainerModifier.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/Containers/RectangleLoopContainerModifier.cs
@@ -32,6 +32,8 @@ public class RectangleLoopContainerModifier : Modifier
///
public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator)
{
+ if (!Enabled) { return; }
+
while (iterator.HasNext)
{
Particle* particle = iterator.Next();
diff --git a/source/MonoGame.Extended/Particles/Modifiers/DragModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/DragModifier.cs
index 923c5649..627b63a8 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/DragModifier.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/DragModifier.cs
@@ -51,6 +51,8 @@ public class DragModifier : Modifier
///
public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator)
{
+ if (!Enabled) { return; }
+
while (iterator.HasNext)
{
Particle* particle = iterator.Next();
diff --git a/source/MonoGame.Extended/Particles/Modifiers/Interpolators/ColorInterpolator.cs b/source/MonoGame.Extended/Particles/Modifiers/Interpolators/ColorInterpolator.cs
index 172bf896..3500e4cb 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/Interpolators/ColorInterpolator.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/Interpolators/ColorInterpolator.cs
@@ -29,6 +29,8 @@ public sealed class ColorInterpolator : Interpolator
/// A pointer to the particle to update.
public override unsafe void Update(float amount, Particle* particle)
{
+ if (!Enabled) { return; }
+
float h = StartValue.X + (EndValue.X - StartValue.X) * amount;
float s = StartValue.Y + (EndValue.Y - StartValue.Y) * amount;
float l = StartValue.Z + (EndValue.Z - StartValue.Z) * amount;
diff --git a/source/MonoGame.Extended/Particles/Modifiers/Interpolators/HueInterpolator.cs b/source/MonoGame.Extended/Particles/Modifiers/Interpolators/HueInterpolator.cs
index ccc03b19..246200f1 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/Interpolators/HueInterpolator.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/Interpolators/HueInterpolator.cs
@@ -23,6 +23,8 @@ public class HueInterpolator : Interpolator
/// A pointer to the particle to update.
public override unsafe void Update(float amount, Particle* particle)
{
+ if (!Enabled) { return; }
+
float h = StartValue + (EndValue - StartValue) * amount;
particle->Color[0] = h;
}
diff --git a/source/MonoGame.Extended/Particles/Modifiers/Interpolators/Interpolator.cs b/source/MonoGame.Extended/Particles/Modifiers/Interpolators/Interpolator.cs
index 4e31d7aa..6e243be0 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/Interpolators/Interpolator.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/Interpolators/Interpolator.cs
@@ -22,12 +22,22 @@ public abstract class Interpolator
///
public string Name;
+ ///
+ /// Indicates if this interpolator is enabled.
+ ///
+ ///
+ /// This value determines if this interpolator is enabled. When an interpolator is disabled, the interpolator is
+ /// not applied to the modifier.
+ ///
+ public bool Enabled;
+
///
/// Initializes a new instance of the class.
///
protected Interpolator()
{
Name = GetType().Name;
+ Enabled = true;
}
///
diff --git a/source/MonoGame.Extended/Particles/Modifiers/Interpolators/OpacityInterpolator.cs b/source/MonoGame.Extended/Particles/Modifiers/Interpolators/OpacityInterpolator.cs
index 2694e450..3de3a00f 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/Interpolators/OpacityInterpolator.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/Interpolators/OpacityInterpolator.cs
@@ -28,6 +28,8 @@ public class OpacityInterpolator : Interpolator
/// A pointer to the particle to update.
public override unsafe void Update(float amount, Particle* particle)
{
+ if (!Enabled) { return; }
+
particle->Opacity = StartValue + (EndValue - StartValue) * amount;
}
}
diff --git a/source/MonoGame.Extended/Particles/Modifiers/Interpolators/RotationInterpolator.cs b/source/MonoGame.Extended/Particles/Modifiers/Interpolators/RotationInterpolator.cs
index c37c49f0..4f3644f5 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/Interpolators/RotationInterpolator.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/Interpolators/RotationInterpolator.cs
@@ -23,6 +23,8 @@ public class RotationInterpolator : Interpolator
/// A pointer to the particle to update.
public override unsafe void Update(float amount, Particle* particle)
{
+ if (!Enabled) { return; }
+
particle->Rotation = StartValue + (EndValue - StartValue) * amount;
}
}
diff --git a/source/MonoGame.Extended/Particles/Modifiers/Interpolators/ScaleInterpolator.cs b/source/MonoGame.Extended/Particles/Modifiers/Interpolators/ScaleInterpolator.cs
index fc6606fa..8cc304d4 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/Interpolators/ScaleInterpolator.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/Interpolators/ScaleInterpolator.cs
@@ -24,6 +24,8 @@ public class ScaleInterpolator : Interpolator
/// A pointer to the particle to update.
public override unsafe void Update(float amount, Particle* particle)
{
+ if (!Enabled) { return; }
+
particle->Scale[0] = StartValue.X + (EndValue.X - StartValue.X) * amount;
particle->Scale[1] = StartValue.Y + (EndValue.Y - StartValue.Y) * amount;
}
diff --git a/source/MonoGame.Extended/Particles/Modifiers/Interpolators/VelocityInterpolator.cs b/source/MonoGame.Extended/Particles/Modifiers/Interpolators/VelocityInterpolator.cs
index 4e0cd2c3..4cacdf23 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/Interpolators/VelocityInterpolator.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/Interpolators/VelocityInterpolator.cs
@@ -24,6 +24,8 @@ public class VelocityInterpolator : Interpolator
/// A pointer to the particle to update.
public override unsafe void Update(float amount, Particle* particle)
{
+ if (!Enabled) { return; }
+
particle->Velocity[0] = StartValue.X + (EndValue.X - StartValue.X) * amount;
particle->Velocity[1] = StartValue.Y + (EndValue.Y - StartValue.Y) * amount;
}
diff --git a/source/MonoGame.Extended/Particles/Modifiers/LinearGravityModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/LinearGravityModifier.cs
index 23d7aa90..e52eec1f 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/LinearGravityModifier.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/LinearGravityModifier.cs
@@ -43,6 +43,8 @@ public class LinearGravityModifier : Modifier
///
public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator)
{
+ if (!Enabled) { return; }
+
Vector2 vector = Direction * (Strength * elapsedSeconds);
while (iterator.HasNext)
diff --git a/source/MonoGame.Extended/Particles/Modifiers/Modifier.cs b/source/MonoGame.Extended/Particles/Modifiers/Modifier.cs
index b0e95ac5..d4ada2cd 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/Modifier.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/Modifier.cs
@@ -17,8 +17,6 @@ public abstract class Modifier
{
private const float DEFAULT_MODIFIER_FREQUENCY = 60.0f;
- private int _particlesUpdatedThisCycle;
-
///
/// Gets or sets the display name of this modifier.
///
@@ -44,6 +42,15 @@ public abstract class Modifier
///
public float Frequency;
+ ///
+ /// Indicates whether this modifier is enabled.
+ ///
+ ///
+ /// This value determines if this modifier is enabled. When a modifier is disabled, the modifier is not applied
+ /// to the particles.
+ ///
+ public bool Enabled;
+
///
/// Initializes a new instance of the class.
///
@@ -55,6 +62,7 @@ public abstract class Modifier
{
Name = GetType().Name;
Frequency = DEFAULT_MODIFIER_FREQUENCY;
+ Enabled = true;
}
///
diff --git a/source/MonoGame.Extended/Particles/Modifiers/OpacityFastFadeModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/OpacityFastFadeModifier.cs
index f6afeb91..5ed57d3b 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/OpacityFastFadeModifier.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/OpacityFastFadeModifier.cs
@@ -34,6 +34,8 @@ public sealed class OpacityFastFadeModifier : Modifier
///
public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator)
{
+ if (!Enabled) { return; }
+
while (iterator.HasNext)
{
Particle* particle = iterator.Next();
diff --git a/source/MonoGame.Extended/Particles/Modifiers/RotationModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/RotationModifier.cs
index 1519196c..21009992 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/RotationModifier.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/RotationModifier.cs
@@ -34,6 +34,8 @@ public class RotationModifier : Modifier
///
public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator)
{
+ if (!Enabled) { return; }
+
float rotationRateDelta = RotationRate * elapsedSeconds;
while (iterator.HasNext)
diff --git a/source/MonoGame.Extended/Particles/Modifiers/VelocityColorModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/VelocityColorModifier.cs
index 8ac790c3..dbef0545 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/VelocityColorModifier.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/VelocityColorModifier.cs
@@ -60,6 +60,8 @@ public class VelocityColorModifier : Modifier
///
public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator)
{
+ if (!Enabled) { return; }
+
float velocityThreshold2 = VelocityThreshold * VelocityThreshold;
while (iterator.HasNext)
diff --git a/source/MonoGame.Extended/Particles/Modifiers/VelocityModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/VelocityModifier.cs
index 8a849841..3f230925 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/VelocityModifier.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/VelocityModifier.cs
@@ -40,6 +40,8 @@ public class VelocityModifier : Modifier
///
public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator)
{
+ if (!Enabled) { return; }
+
float velocityThreshold2 = VelocityThreshold * VelocityThreshold;
while (iterator.HasNext)
diff --git a/source/MonoGame.Extended/Particles/Modifiers/VortexModifier.cs b/source/MonoGame.Extended/Particles/Modifiers/VortexModifier.cs
index 7b573959..21fa1676 100644
--- a/source/MonoGame.Extended/Particles/Modifiers/VortexModifier.cs
+++ b/source/MonoGame.Extended/Particles/Modifiers/VortexModifier.cs
@@ -54,6 +54,8 @@ public unsafe class VortexModifier : Modifier
///
public override unsafe void Update(float elapsedSeconds, ParticleIterator iterator)
{
+ if (!Enabled) { return; }
+
while (iterator.HasNext)
{
Particle* particle = iterator.Next();
diff --git a/source/MonoGame.Extended/Particles/ParticleEffect.cs b/source/MonoGame.Extended/Particles/ParticleEffect.cs
index ad1c76ee..acc5aa13 100644
--- a/source/MonoGame.Extended/Particles/ParticleEffect.cs
+++ b/source/MonoGame.Extended/Particles/ParticleEffect.cs
@@ -23,6 +23,8 @@ namespace MonoGame.Extended.Particles;
///
public class ParticleEffect : IDisposable
{
+ private float _nextAutoTrigger;
+
///
/// Gets or sets the name of this effect, used for identification and debugging.
///
@@ -55,6 +57,24 @@ public class ParticleEffect : IDisposable
///
public Vector2 Scale;
+ ///
+ /// A value indicating whether this particle effect should automatically trigger its particle emitters.
+ ///
+ ///
+ /// When , all emitters of this will be triggered at the same
+ /// based on the . When , users will need to manually call
+ /// the method to trigger emitters.
+ ///
+ public bool AutoTrigger;
+
+ ///
+ /// The frequency, in seconds, at which this automatically triggers emitters.
+ ///
+ ///
+ /// If is , this value is ignored.
+ ///
+ public float AutoTriggerFrequency;
+
///
/// Gets or sets the collection of emitters that compose this effect.
///
@@ -97,6 +117,8 @@ public class ParticleEffect : IDisposable
Rotation = 0.0f;
Scale = Vector2.One;
Emitters = new List();
+ AutoTrigger = true;
+ AutoTriggerFrequency = 1.0f;
}
///
@@ -128,6 +150,13 @@ public class ParticleEffect : IDisposable
}
}
+ ///
+ /// Updates the state of all emitters in this effect.
+ ///
+ /// The timing values for the current update cycle.
+ ///
+ /// Thrown if this method is called after the effect has been disposed.
+ ///
public void Update(GameTime gameTime)
{
Update((float)gameTime.ElapsedGameTime.TotalSeconds);
@@ -137,10 +166,6 @@ public class ParticleEffect : IDisposable
/// Updates the state of all emitters in this effect.
///
/// The elapsed time, in seconds, since the last update.
- ///
- /// This method propagates the update call to each emitter in the effect, passing along
- /// the elapsed time and the current position of the effect.
- ///
///
/// Thrown if this method is called after the effect has been disposed.
///
@@ -148,6 +173,17 @@ public class ParticleEffect : IDisposable
{
ObjectDisposedException.ThrowIf(IsDisposed, typeof(ParticleBuffer));
+ if (AutoTrigger)
+ {
+ _nextAutoTrigger -= elapsedSeconds;
+
+ if (_nextAutoTrigger <= 0)
+ {
+ Trigger();
+ _nextAutoTrigger += AutoTriggerFrequency;
+ }
+ }
+
for (int i = 0; i < Emitters.Count; i++)
{
Emitters[i].Update(elapsedSeconds, Position);
diff --git a/source/MonoGame.Extended/Particles/ParticleEffectReader.cs b/source/MonoGame.Extended/Particles/ParticleEffectReader.cs
index 6a6716c3..42f22d6d 100644
--- a/source/MonoGame.Extended/Particles/ParticleEffectReader.cs
+++ b/source/MonoGame.Extended/Particles/ParticleEffectReader.cs
@@ -34,12 +34,12 @@ public sealed class ParticleEffectReader : IDisposable
public bool IsDisposed { get; private set; }
///
- /// Initializes a new instance of the class that reads from a file.
+ /// Initializes a new instance of the class that reads from a file.
///
/// The file path to read the XMl from.
/// The to use for loading textures.
/// is
- /// is or empty.
+ /// is or empty.
public ParticleEffectReader(string fileName, ContentManager content)
{
ArgumentNullException.ThrowIfNull(content);
@@ -97,8 +97,11 @@ public sealed class ParticleEffectReader : IDisposable
string name = _reader.GetAttribute(nameof(ParticleEffect.Name)) ?? "Unnamed";
ParticleEffect effect = new ParticleEffect(name);
+ effect.Position = _reader.GetAttributeVector2(nameof(ParticleEffect.Position));
effect.Rotation = _reader.GetAttributeFloat(nameof(ParticleEffect.Rotation));
effect.Scale = _reader.GetAttributeVector2(nameof(ParticleEffect.Scale));
+ effect.AutoTrigger = _reader.GetAttributeBool(nameof(ParticleEffect.AutoTrigger));
+ effect.AutoTriggerFrequency = _reader.GetAttributeFloat(nameof(ParticleEffect.AutoTriggerFrequency));
if (_reader.ReadToDescendant(nameof(ParticleEffect.Emitters)))
{
@@ -124,8 +127,6 @@ public sealed class ParticleEffectReader : IDisposable
emitter.LifeSpan = _reader.GetAttributeFloat(nameof(ParticleEmitter.LifeSpan));
emitter.Offset = _reader.GetAttributeVector2(nameof(ParticleEmitter.Offset));
emitter.LayerDepth = _reader.GetAttributeFloat(nameof(ParticleEmitter.LayerDepth));
- emitter.AutoTrigger = _reader.GetAttributeBool(nameof(ParticleEmitter.AutoTrigger));
- emitter.AutoTriggerFrequency = _reader.GetAttributeFloat(nameof(ParticleEmitter.AutoTriggerFrequency));
emitter.ReclaimFrequency = _reader.GetAttributeFloat(nameof(ParticleEmitter.ReclaimFrequency));
string strategy = _reader.GetAttribute(nameof(ParticleEmitter.ModifierExecutionStrategy));
@@ -400,13 +401,6 @@ public sealed class ParticleEffectReader : IDisposable
lineProfile.Length = reader.GetAttributeFloat(nameof(LineProfile.Length));
return lineProfile;
- case nameof(LineUniformProfile):
- LineUniformProfile lineUniformProfile = new LineUniformProfile();
- lineUniformProfile.Axis = reader.GetAttributeVector2(nameof(LineUniformProfile.Axis));
- lineUniformProfile.Length = reader.GetAttributeFloat(nameof(LineUniformProfile.Length));
- lineUniformProfile.PerpendicularDirection = reader.GetAttributeVector2(nameof(LineUniformProfile.PerpendicularDirection));
- return lineUniformProfile;
-
case nameof(PointProfile):
return Profile.Point();
diff --git a/source/MonoGame.Extended/Particles/ParticleEffectWriter.cs b/source/MonoGame.Extended/Particles/ParticleEffectWriter.cs
index e02174a5..5158f92f 100644
--- a/source/MonoGame.Extended/Particles/ParticleEffectWriter.cs
+++ b/source/MonoGame.Extended/Particles/ParticleEffectWriter.cs
@@ -75,6 +75,8 @@ public class ParticleEffectWriter : IDisposable
_writer.WriteAttributeVector2(nameof(ParticleEffect.Position), effect.Position);
_writer.WriteAttributeFloat(nameof(ParticleEffect.Rotation), effect.Rotation);
_writer.WriteAttributeVector2(nameof(ParticleEffect.Scale), effect.Scale);
+ _writer.WriteAttributeBool(nameof(ParticleEffect.AutoTrigger), effect.AutoTrigger);
+ _writer.WriteAttributeFloat(nameof(ParticleEffect.AutoTriggerFrequency), effect.AutoTriggerFrequency);
if (effect.Emitters.Count > 0)
{
@@ -100,8 +102,6 @@ public class ParticleEffectWriter : IDisposable
_writer.WriteAttributeFloat(nameof(ParticleEmitter.LifeSpan), emitter.LifeSpan);
_writer.WriteAttributeVector2(nameof(ParticleEmitter.Offset), emitter.Offset);
_writer.WriteAttributeFloat(nameof(ParticleEmitter.LayerDepth), emitter.LayerDepth);
- _writer.WriteAttributeBool(nameof(ParticleEmitter.AutoTrigger), emitter.AutoTrigger);
- _writer.WriteAttributeFloat(nameof(ParticleEmitter.AutoTriggerFrequency), emitter.AutoTriggerFrequency);
_writer.WriteAttributeFloat(nameof(ParticleEmitter.ReclaimFrequency), emitter.ReclaimFrequency);
_writer.WriteAttributeInt(nameof(ParticleEmitter.Capacity), emitter.Capacity);
_writer.WriteAttributeString(nameof(ParticleEmitter.ModifierExecutionStrategy), emitter.ModifierExecutionStrategy.ToString());
@@ -261,13 +261,6 @@ public class ParticleEffectWriter : IDisposable
_writer.WriteAttributeFloat(nameof(LineProfile.Length), lineProfile.Length);
break;
- case LineUniformProfile lineUniformProfile:
- _writer.WriteAttributeString(nameof(Type), nameof(LineUniformProfile));
- _writer.WriteAttributeVector2(nameof(LineUniformProfile.Axis), lineUniformProfile.Axis);
- _writer.WriteAttributeFloat(nameof(LineUniformProfile.Length), lineUniformProfile.Length);
- _writer.WriteAttributeVector2(nameof(LineUniformProfile.PerpendicularDirection), lineUniformProfile.PerpendicularDirection);
- break;
-
case PointProfile:
_writer.WriteAttributeString(nameof(Type), nameof(PointProfile));
break;
diff --git a/source/MonoGame.Extended/Particles/ParticleEmitter.cs b/source/MonoGame.Extended/Particles/ParticleEmitter.cs
index 6513e603..07313fb9 100644
--- a/source/MonoGame.Extended/Particles/ParticleEmitter.cs
+++ b/source/MonoGame.Extended/Particles/ParticleEmitter.cs
@@ -26,7 +26,6 @@ public sealed unsafe class ParticleEmitter : IDisposable
{
private float _totalSeconds;
private float _secondsSinceLastReclaim;
- private float _nextAutoTrigger;
///
/// Gets or sets the buffer that stores and manages the particles for this emitter.
@@ -88,23 +87,6 @@ public sealed unsafe class ParticleEmitter : IDisposable
///
public float LayerDepth;
- ///
- /// Gets or sets a value indicating whether this emitter should automatically trigger particle emissions.
- ///
- ///
- /// When set to , the emitter will periodically emit particles based on the
- /// property, without requiring explicit calls to .
- ///
- public bool AutoTrigger;
-
- ///
- /// Gets or sets the frequency, in seconds, at which this emitter automatically triggers particle emissions.
- ///
- ///
- /// This property only has an effect when is set to .
- ///
- public float AutoTriggerFrequency;
-
///
/// Gets or sets the frequency, in times per second, at which expired particles are reclaimed.
///
@@ -162,6 +144,11 @@ public sealed unsafe class ParticleEmitter : IDisposable
///
public ParticleRenderingOrder RenderingOrder;
+ ///
+ /// Indicates whether the particles emitted by this emitter are visible.
+ ///
+ public bool Visible;
+
///
/// Gets a value indicating whether this has been disposed.
///
@@ -197,8 +184,7 @@ public sealed unsafe class ParticleEmitter : IDisposable
ReclaimFrequency = 60.0f;
Offset = Vector2.Zero;
LayerDepth = 0.0f;
- AutoTrigger = true;
- AutoTriggerFrequency = 1.0f;
+ Visible = true;
}
///
@@ -256,17 +242,6 @@ public sealed unsafe class ParticleEmitter : IDisposable
_totalSeconds += elapsedSeconds;
_secondsSinceLastReclaim += elapsedSeconds;
- if (AutoTrigger)
- {
- _nextAutoTrigger -= elapsedSeconds;
-
- if (_nextAutoTrigger <= 0)
- {
- Trigger(position, LayerDepth);
- _nextAutoTrigger = AutoTriggerFrequency;
- }
- }
-
if (Buffer.Count == 0)
{
return;
diff --git a/source/MonoGame.Extended/Particles/Profiles/LineUniformProfile.cs b/source/MonoGame.Extended/Particles/Profiles/LineUniformProfile.cs
deleted file mode 100644
index 1a585e6f..00000000
--- a/source/MonoGame.Extended/Particles/Profiles/LineUniformProfile.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) Craftwork Games. All rights reserved.
-// Licensed under the MIT license.
-// See LICENSE file in the project root for full license information.
-
-using Microsoft.Xna.Framework;
-
-namespace MonoGame.Extended.Particles.Profiles;
-
-///
-/// A profile that distributes particles uniformly along a line segment with a fixed heading direction.
-///
-///
-/// The positions particles randomly along a line segment centered at the emitter
-/// position and defined by an axis direction and length. Unlike other profiles, this profile uses a fixed heading
-/// direction for all particles, perpendicular to the line.
-///
-public sealed class LineUniformProfile : Profile
-{
- ///
- /// The direction vector of the line axis.
- ///
- public Vector2 Axis;
-
- ///
- /// The length of the line segment.
- ///
- public float Length;
-
- ///
- /// The fixed heading direction for all particles spawned from this profile.
- ///
- public Vector2 PerpendicularDirection;
-
- ///
- /// Computes the offset and heading for a new particle.
- ///
- /// A pointer to the Vector2 where the offset from the emitter position will be stored.
- /// A pointer to the Vector2 where the unit direction vector will be stored.
- public override unsafe void GetOffsetAndHeading(Vector2* offset, Vector2* heading)
- {
- // 1. Spawn the particle at a random point on the line axis
- float value = FastRandom.Shared.NextSingle(Length * -0.5f, Length * 0.5f);
- offset->X = Axis.X * value;
- offset->Y = Axis.Y * value;
-
- // 2. Set the heading to the perpendicular direction
- *heading = PerpendicularDirection;
- }
-
- ///
- /// Sets the property to a normalized version of the specified vector.
- ///
- /// The direction vector to normalize and use as the perpendicular direction.
- public void SetPerpendicularDirection(Vector2 direction)
- {
- PerpendicularDirection = Vector2.Normalize(direction);
- }
-}
diff --git a/source/MonoGame.Extended/Particles/Profiles/Profile.cs b/source/MonoGame.Extended/Particles/Profiles/Profile.cs
index 2b0aedc7..515c971f 100644
--- a/source/MonoGame.Extended/Particles/Profiles/Profile.cs
+++ b/source/MonoGame.Extended/Particles/Profiles/Profile.cs
@@ -43,18 +43,6 @@ public abstract class Profile
return new LineProfile { Axis = axis, Length = length };
}
- ///
- /// Creates a that emits particles uniformly along a line segment with a fixed heading.
- ///
- /// The direction vector of the line axis.
- /// The length fo the line segment.
- /// The fixed heading direction for all particles spawned from the profile.
- ///
- public static Profile LineUniform(Vector2 axis, float length, Vector2 perpendicularDirection)
- {
- return new LineUniformProfile { Axis = axis, Length = length, PerpendicularDirection = perpendicularDirection };
- }
-
///
/// Creates a that emits particles from the perimeter of a circle.
///
diff --git a/source/MonoGame.Extended/Particles/SpriteBatchExtensions.ParticleEfffect.cs b/source/MonoGame.Extended/Particles/SpriteBatchExtensions.ParticleEfffect.cs
index 86e28100..4d4dff53 100644
--- a/source/MonoGame.Extended/Particles/SpriteBatchExtensions.ParticleEfffect.cs
+++ b/source/MonoGame.Extended/Particles/SpriteBatchExtensions.ParticleEfffect.cs
@@ -1,5 +1,4 @@
using System;
-
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MonoGame.Extended.Graphics;
@@ -7,8 +6,18 @@ using MonoGame.Extended.Particles.Data;
namespace MonoGame.Extended.Particles;
+///
+/// Provides extension methods for to draw particle effects and emitters.
+///
public static class SpriteBatchExtensions
{
+ ///
+ /// Draws a particle effect by rendering all of its active emitters.
+ ///
+ /// The used for drawing.
+ /// The to draw.
+ /// Thrown when is null.
+ /// Thrown when is disposed.
public static void Draw(this SpriteBatch spriteBatch, ParticleEffect effect)
{
ArgumentNullException.ThrowIfNull(effect);
@@ -20,6 +29,13 @@ public static class SpriteBatchExtensions
}
}
+ ///
+ /// Draws a particle emitter by rendering all of its active particles.
+ ///
+ /// The used for drawing.
+ /// The to draw.
+ /// Thrown when is null.
+ /// Thrown when is disposed.
public static void Draw(this SpriteBatch spriteBatch, ParticleEmitter emitter)
{
ArgumentNullException.ThrowIfNull(emitter);
@@ -43,6 +59,12 @@ public static class SpriteBatchExtensions
return;
}
+ // Early exit if the emitter is not visible
+ if (!emitter.Visible)
+ {
+ return;
+ }
+
Texture2DRegion region = emitter.TextureRegion;
Texture2D texture = region.Texture;
Rectangle sourceRect = region.Bounds;
diff --git a/tests/MonoGame.Extended.Tests/Particles/ParticleEffectReaderTests.cs b/tests/MonoGame.Extended.Tests/Particles/ParticleEffectReaderTests.cs
index 5fd3a6cc..9add2aeb 100644
--- a/tests/MonoGame.Extended.Tests/Particles/ParticleEffectReaderTests.cs
+++ b/tests/MonoGame.Extended.Tests/Particles/ParticleEffectReaderTests.cs
@@ -61,7 +61,7 @@ public class ParticleEffectReaderTests
string xml =
"""
-
+
""";
ParticleEffect effect = ReadParticleEffectFromXml(xml);
@@ -71,6 +71,8 @@ public class ParticleEffectReaderTests
Assert.Equal(0.0f, effect.Rotation);
Assert.Equal(Vector2.One, effect.Scale);
Assert.Empty(effect.Emitters);
+ Assert.True(effect.AutoTrigger);
+ Assert.Equal(effect.AutoTriggerFrequency, 1.0f);
}
[Fact]
@@ -79,9 +81,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -111,9 +113,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -147,9 +149,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -181,9 +183,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -215,9 +217,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -249,9 +251,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -283,9 +285,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -311,49 +313,15 @@ public class ParticleEffectReaderTests
Assert.Equal(1.0f, profile.Length);
}
- [Fact]
- public void ReadParticleEffect_LineUniformProfile_ReadsExpected()
- {
- string xml =
- $"""
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- """;
-
- ParticleEffect effect = ReadParticleEffectFromXml(xml);
-
- Assert.Single(effect.Emitters);
- ParticleEmitter emitter = effect.Emitters[0];
-
- LineUniformProfile profile = Assert.IsType(emitter.Profile);
- Assert.Equal(Vector2.One, profile.Axis);
- Assert.Equal(1.0f, profile.Length);
- }
-
[Fact]
public void ReadParticleEffect_PointProfile_ReadsExpected()
{
string xml =
$"""
-
+
-
+
@@ -383,9 +351,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -417,9 +385,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -451,9 +419,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -490,9 +458,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -532,9 +500,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -573,9 +541,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -614,9 +582,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -653,9 +621,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -695,9 +663,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -736,9 +704,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -776,9 +744,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -818,9 +786,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -858,9 +826,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -900,9 +868,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -946,9 +914,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -992,9 +960,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -1038,9 +1006,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -1084,9 +1052,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
@@ -1130,9 +1098,9 @@ public class ParticleEffectReaderTests
string xml =
$"""
-
+
-
+
diff --git a/tests/MonoGame.Extended.Tests/Particles/ParticleEffectWriterTests.cs b/tests/MonoGame.Extended.Tests/Particles/ParticleEffectWriterTests.cs
index 3f963fc3..f6d2cc32 100644
--- a/tests/MonoGame.Extended.Tests/Particles/ParticleEffectWriterTests.cs
+++ b/tests/MonoGame.Extended.Tests/Particles/ParticleEffectWriterTests.cs
@@ -31,7 +31,7 @@ public class ParticleEffectWriterTests
string expected =
$"""
-
+
""";
AssertParticleEffect(effect, expected);
@@ -48,9 +48,9 @@ public class ParticleEffectWriterTests
string expected =
$"""
-
+
-
+
@@ -82,9 +82,9 @@ public class ParticleEffectWriterTests
string expected =
$"""
-
+
-
+
@@ -119,9 +119,9 @@ public class ParticleEffectWriterTests
string expected =
$"""
-
+
-
+
@@ -153,9 +153,9 @@ public class ParticleEffectWriterTests
string expected =
$"""
-
+
-
+
@@ -187,9 +187,9 @@ public class ParticleEffectWriterTests
string expected =
$"""
-
+
-
+
@@ -221,9 +221,9 @@ public class ParticleEffectWriterTests
string expected =
$"""
-
+
-
+
@@ -255,9 +255,9 @@ public class ParticleEffectWriterTests
string expected =
$"""
-
+
-
+
@@ -276,40 +276,6 @@ public class ParticleEffectWriterTests
AssertParticleEffect(effect, expected);
}
- [Fact]
- public void WriteParticleEffect_LineUniformProfile_WritesExpected()
- {
- ParticleEffect effect = new ParticleEffect("TestEffect");
- ParticleEmitter emitter = new ParticleEmitter(1);
- emitter.Name = "TestEmitter";
- emitter.Profile = Profile.LineUniform(Vector2.One, 1.0f, Vector2.One);
- effect.Emitters.Add(emitter);
-
-
- string expected =
- $"""
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- """;
-
- AssertParticleEffect(effect, expected);
- }
-
[Fact]
public void WriteParticleEffect_PointProfile_WritesExpected()
{
@@ -323,9 +289,9 @@ public class ParticleEffectWriterTests
string expected =
$"""
-
+
-
+
@@ -357,9 +323,9 @@ public class ParticleEffectWriterTests
string expected =
$"""
-
+
-
+
@@ -391,9 +357,9 @@ public class ParticleEffectWriterTests
string expected =
$"""
-
+
-
+
@@ -427,9 +393,9 @@ public class ParticleEffectWriterTests
string expected =
$"""
-
+
-
+
@@ -466,9 +432,9 @@ public class ParticleEffectWriterTests
string expected =
$"""
-
+
-
+
@@ -505,9 +471,9 @@ public class ParticleEffectWriterTests
string expected =
$"""
-
+
-
+
@@ -544,9 +510,9 @@ public class ParticleEffectWriterTests
string expected =
$"""
-
+