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 = $""" - + - + @@ -583,9 +549,9 @@ public class ParticleEffectWriterTests string expected = $""" - + - + @@ -622,9 +588,9 @@ public class ParticleEffectWriterTests string expected = $""" - + - + @@ -661,9 +627,9 @@ public class ParticleEffectWriterTests string expected = $""" - + - + @@ -700,9 +666,9 @@ public class ParticleEffectWriterTests string expected = $""" - + - + @@ -739,9 +705,9 @@ public class ParticleEffectWriterTests string expected = $""" - + - + @@ -778,9 +744,9 @@ public class ParticleEffectWriterTests string expected = $""" - + - + @@ -817,9 +783,9 @@ public class ParticleEffectWriterTests string expected = $""" - + - + @@ -858,9 +824,9 @@ public class ParticleEffectWriterTests string expected = $""" - + - + @@ -903,9 +869,9 @@ public class ParticleEffectWriterTests string expected = $""" - + - + @@ -948,9 +914,9 @@ public class ParticleEffectWriterTests string expected = $""" - + - + @@ -993,9 +959,9 @@ public class ParticleEffectWriterTests string expected = $""" - + - + @@ -1038,9 +1004,9 @@ public class ParticleEffectWriterTests string expected = $""" - + - + @@ -1083,9 +1049,9 @@ public class ParticleEffectWriterTests string expected = $""" - + - +