Add Independent Axis Scale To Particles (#996)

* Make it so that the particles can scale independently on the x and y axis

* Close element

* Update tests with changes
This commit is contained in:
Christopher Whitley
2025-06-10 22:58:17 -04:00
committed by GitHub
parent 5fea9192f2
commit ccd0c5ba2f
9 changed files with 116 additions and 72 deletions
@@ -44,7 +44,7 @@ public unsafe struct Particle
/// <summary>
/// The scale factor applied to this particle's visual representation.
/// </summary>
public float Scale;
public fixed float Scale[2];
/// <summary>
/// The position where this particle was triggered or emitted from [X, Y].
@@ -54,7 +54,7 @@ public class ParticleReleaseParameters
/// <remarks>
/// Defaults to a random value between 0.0 (half scale) and 1.0 (full scale)
/// </remarks>
public ParticleFloatParameter Scale = new ParticleFloatParameter(0.5f, 1.0f);
public ParticleVector2Parameter Scale = new ParticleVector2Parameter(new Vector2(0.5f, 0.5f), new Vector2(1.0f, 1.0f));
/// <summary>
/// Gets or sets the initial rotation (in radians) of particles when released.
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
// See LICENSE file in the project root for full license information.
using Microsoft.Xna.Framework;
using MonoGame.Extended.Particles.Data;
namespace MonoGame.Extended.Particles.Modifiers.Interpolators;
@@ -14,7 +15,7 @@ namespace MonoGame.Extended.Particles.Modifiers.Interpolators;
/// <see cref="Interpolator{T}.StartValue"/> to <see cref="Interpolator{T}.EndValue"/> based on the
/// provided interpolation amount (typically representing the particle's normalized age).
/// </remarks>
public class ScaleInterpolator : Interpolator<float>
public class ScaleInterpolator : Interpolator<Vector2>
{
/// <summary>
/// Updates a particle's scale by interpolating between the start and end values.
@@ -23,6 +24,7 @@ public class ScaleInterpolator : Interpolator<float>
/// <param name="particle">A pointer to the particle to update.</param>
public override unsafe void Update(float amount, Particle* particle)
{
particle->Scale = StartValue + (EndValue - StartValue) * amount;
particle->Scale[0] = StartValue.X + (EndValue.X - StartValue.X) * amount;
particle->Scale[1] = StartValue.Y + (EndValue.Y - StartValue.Y) * amount;
}
}
@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Xml;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
@@ -270,7 +271,7 @@ public sealed class ParticleEffectReader : IDisposable
break;
case nameof(ParticleReleaseParameters.Scale):
parameters.Scale = ReadParticleFloatParameter(subtree);
parameters.Scale = ReadParticleVector2Parameter(subtree);
break;
case nameof(ParticleReleaseParameters.Rotation):
@@ -325,6 +326,25 @@ public sealed class ParticleEffectReader : IDisposable
return new ParticleFloatParameter(0);
}
private ParticleVector2Parameter ReadParticleVector2Parameter(XmlReader reader)
{
ParticleValueKind kind = reader.GetAttributeEnum<ParticleValueKind>(nameof(ParticleVector2Parameter.Kind));
if(kind == ParticleValueKind.Constant)
{
Vector2 value = reader.GetAttributeVector2(nameof(ParticleVector2Parameter.Constant));
return new ParticleVector2Parameter(value);
}
else if(kind == ParticleValueKind.Random)
{
Vector2 min = reader.GetAttributeVector2(nameof(ParticleVector2Parameter.RandomMin));
Vector2 max = reader.GetAttributeVector2(nameof(ParticleVector2Parameter.RandomMax));
return new ParticleVector2Parameter(min, max);
}
return new ParticleVector2Parameter(Vector2.Zero);
}
private ParticleColorParameter ReadParticleColorParameter(XmlReader reader)
{
ParticleValueKind kind = reader.GetAttributeEnum<ParticleValueKind>(nameof(ParticleColorParameter.Kind));
@@ -604,8 +624,8 @@ public sealed class ParticleEffectReader : IDisposable
case nameof(ScaleInterpolator):
ScaleInterpolator scaleInterpolator = new ScaleInterpolator();
scaleInterpolator.StartValue = reader.GetAttributeFloat(nameof(ScaleInterpolator.StartValue));
scaleInterpolator.EndValue = reader.GetAttributeFloat(nameof(ScaleInterpolator.EndValue));
scaleInterpolator.StartValue = reader.GetAttributeVector2(nameof(ScaleInterpolator.StartValue));
scaleInterpolator.EndValue = reader.GetAttributeVector2(nameof(ScaleInterpolator.EndValue));
return scaleInterpolator;
case nameof(VelocityInterpolator):
@@ -149,7 +149,7 @@ public class ParticleEffectWriter : IDisposable
WriteParticleFloatParameter(nameof(ParticleReleaseParameters.Speed), parameters.Speed);
WriteParticleColorParameter(nameof(ParticleReleaseParameters.Color), parameters.Color);
WriteParticleFloatParameter(nameof(ParticleReleaseParameters.Opacity), parameters.Opacity);
WriteParticleFloatParameter(nameof(ParticleReleaseParameters.Scale), parameters.Scale);
WriteParticleVector2Parameter(nameof(ParticleReleaseParameters.Scale), parameters.Scale);
WriteParticleFloatParameter(nameof(ParticleReleaseParameters.Rotation), parameters.Rotation);
WriteParticleFloatParameter(nameof(ParticleReleaseParameters.Mass), parameters.Mass);
@@ -191,6 +191,24 @@ public class ParticleEffectWriter : IDisposable
_writer.WriteEndElement();
}
private void WriteParticleVector2Parameter(string name, ParticleVector2Parameter parameter)
{
_writer.WriteStartElement(name);
_writer.WriteAttributeString(nameof(ParticleVector2Parameter.Kind), parameter.Kind.ToString());
if(parameter.Kind == ParticleValueKind.Constant)
{
_writer.WriteAttributeVector2(nameof(ParticleVector2Parameter.Constant), parameter.Constant);
}
else
{
_writer.WriteAttributeVector2(nameof(ParticleVector2Parameter.RandomMin), parameter.RandomMin);
_writer.WriteAttributeVector2(nameof(ParticleVector2Parameter.RandomMax), parameter.RandomMax);
}
_writer.WriteEndElement();
}
private void WriteParticleColorParameter(string name, ParticleColorParameter parameter)
{
_writer.WriteStartElement(name);
@@ -403,8 +421,8 @@ public class ParticleEffectWriter : IDisposable
case ScaleInterpolator scaleInterpolator:
_writer.WriteAttributeString(nameof(Type), nameof(ScaleInterpolator));
_writer.WriteAttributeFloat(nameof(ScaleInterpolator.StartValue), scaleInterpolator.StartValue);
_writer.WriteAttributeFloat(nameof(ScaleInterpolator.EndValue), scaleInterpolator.EndValue);
_writer.WriteAttributeVector2(nameof(ScaleInterpolator.StartValue), scaleInterpolator.StartValue);
_writer.WriteAttributeVector2(nameof(ScaleInterpolator.EndValue), scaleInterpolator.EndValue);
break;
case VelocityInterpolator velocityInterpolator:
@@ -369,7 +369,11 @@ public sealed unsafe class ParticleEmitter : IDisposable
particle->Color[2] = color.Z;
particle->Opacity = Parameters.Opacity.Value;
particle->Scale = Parameters.Scale.Value;
Vector2 scale = Parameters.Scale.Value;
particle->Scale[0] = scale.X;
particle->Scale[1] = scale.Y;
particle->Rotation = Parameters.Rotation.Value;
particle->Mass = Parameters.Mass.Value;
particle->LayerDepth = layerDepth;
@@ -96,7 +96,7 @@ public static class SpriteBatchExtensions
}
Vector2 position = new Vector2(particle->Position[0], particle->Position[1]);
float scale = particle->Scale;
Vector2 scale = new Vector2(particle->Scale[0], particle->Scale[1]);
float rotation = particle->Rotation;
float layerDepth = particle->LayerDepth;