From db7f01dca1660d4f5e73603192100c2ea6616630 Mon Sep 17 00:00:00 2001 From: Michel Fedde Date: Tue, 11 May 2021 21:44:45 +0200 Subject: [PATCH] Rewrote the particles to allow for more advanced particles. + Particles can now be detached from the object. + Each particle now has an own Lifetime, that can be controlled. + Particles can now appear in a continuous way. --- .../Drawing/Particles/ParticleContext.cs | 24 ---- .../Drawing/Particles/ParticleDrawingBasis.cs | 130 +++++++++++++----- .../Drawing/Particles/ParticleInstance.cs | 38 +++++ .../Drawing/Particles/ParticleMovement.cs | 8 +- .../Drawing/Particles/ParticleStruct.cs | 30 ---- SMCode/SM.Base/SM.Base.csproj | 3 +- SMCode/SM.Base/Time/Timer.cs | 2 +- SMCode/SM2D/Drawing/DrawParticles.cs | 23 +++- SM_TEST/Program.cs | 22 +-- 9 files changed, 171 insertions(+), 109 deletions(-) delete mode 100644 SMCode/SM.Base/Drawing/Particles/ParticleContext.cs create mode 100644 SMCode/SM.Base/Drawing/Particles/ParticleInstance.cs delete mode 100644 SMCode/SM.Base/Drawing/Particles/ParticleStruct.cs diff --git a/SMCode/SM.Base/Drawing/Particles/ParticleContext.cs b/SMCode/SM.Base/Drawing/Particles/ParticleContext.cs deleted file mode 100644 index 67f6e47..0000000 --- a/SMCode/SM.Base/Drawing/Particles/ParticleContext.cs +++ /dev/null @@ -1,24 +0,0 @@ -#region usings - -using SM.Base.Time; - -#endregion - -namespace SM.Base.Drawing.Particles -{ - /// - /// A context, with that the particle system sends the information for the movement function. - /// - public struct ParticleContext - { - /// - /// The Timer of the particles - /// - public Timer Timer; - - /// - /// The current speed of the particles. - /// - public float Speed; - } -} \ No newline at end of file diff --git a/SMCode/SM.Base/Drawing/Particles/ParticleDrawingBasis.cs b/SMCode/SM.Base/Drawing/Particles/ParticleDrawingBasis.cs index 24643db..c49359e 100644 --- a/SMCode/SM.Base/Drawing/Particles/ParticleDrawingBasis.cs +++ b/SMCode/SM.Base/Drawing/Particles/ParticleDrawingBasis.cs @@ -18,6 +18,18 @@ namespace SM.Base.Drawing.Particles where TTransform : GenericTransformation, new() where TDirection : struct { + private float? _continuesIntervalSeconds = null; + private Interval _continuesInterval; + + /// + /// The stopwatch of the particles. + /// + protected Timer timer; + + /// + /// This contains the different instances for the particles. + /// + protected List> instances; /// /// The amount of particles @@ -25,24 +37,38 @@ namespace SM.Base.Drawing.Particles public int Amount = 32; /// - /// This contains the different instances for the particles. + /// The base lifetime for particles in seconds. /// - protected List instances; + public float Lifetime; + /// + /// Ranomizes the lifetime for particles. + /// + public float LifetimeRandomize = 0; + + /// + /// If set to any value (except null), it will create the particles continuously. + /// + public float? ContinuousInterval + { + get => _continuesIntervalSeconds; + set + { + if (value.HasValue) + { + _continuesInterval.Target = value.Value; + } + + _continuesIntervalSeconds = value; + } + } + + public bool DetachedParticles; /// /// The maximum speed of the particles + /// Default: 25 /// - public float MaxSpeed = 50; - - /// - /// This contains all important information for each particle. - /// - protected ParticleStruct[] particleStructs; - - /// - /// The stopwatch of the particles. - /// - protected Timer timer; + public float MaxSpeed = 25; /// /// Sets up the timer. @@ -51,6 +77,10 @@ namespace SM.Base.Drawing.Particles protected ParticleDrawingBasis(TimeSpan duration) { timer = new Timer(duration); + _continuesInterval = new Interval(0); + _continuesInterval.End += CreateContinuesParticles; + + Lifetime = (float) duration.TotalSeconds; } /// @@ -65,27 +95,29 @@ namespace SM.Base.Drawing.Particles /// /// Controls the movement of each particles. /// - public abstract Func MovementCalculation { get; set; } + public abstract Func, TDirection> MovementCalculation { get; set; } /// public bool UpdateActive { - get => timer.Active; + get => timer.Active || _continuesInterval.Active; set { return; } } /// public void Update(UpdateContext context) { - ParticleContext particleContext = new ParticleContext - { - Timer = timer - }; - for (int i = 0; i < Amount; i++) + for (int i = 0; i < instances.Count; i++) { - particleContext.Speed = particleStructs[i].Speed; - instances[i].ModelMatrix = CreateMatrix(particleStructs[i], - MovementCalculation(particleStructs[i].Direction, particleContext)); + instances[i].Lifetime -= context.Deltatime; + if (instances[i].Lifetime <= 0) + { + instances.Remove(instances[i]); + break; + } + + instances[i].ModelMatrix = CreateMatrix(instances[i], + MovementCalculation(instances[i])); } } @@ -94,19 +126,50 @@ namespace SM.Base.Drawing.Particles /// public void Trigger() { + instances = new List>(); + if (_continuesIntervalSeconds.HasValue) + { + _continuesInterval.Target = _continuesIntervalSeconds.Value; + _continuesInterval.Start(); + + return; + } + timer.Start(); CreateParticles(); } + /// + /// Stops the particles. + /// + public void Stop() + { + if (_continuesInterval.Active) + { + _continuesInterval.Stop(); + } + + timer.Stop(); + } + + public override void OnRemoved(object sender) + { + base.OnRemoved(sender); + + Stop(); + } + /// protected override void DrawContext(ref DrawContext context) { - if (!timer.Active) return; + if (!timer.Active && _continuesInterval != null && !_continuesInterval.Active) return; base.DrawContext(ref context); - context.Instances = instances; + if (DetachedParticles) context.ModelMatrix = Matrix4.Identity; + + context.Instances = instances.ConvertAll(a => (Instance)a); context.Shader.Draw(context); } @@ -116,24 +179,27 @@ namespace SM.Base.Drawing.Particles /// protected virtual void CreateParticles() { - particleStructs = new ParticleStruct[Amount]; - instances = new List(); + + for (int i = 0; i < Amount; i++) { - particleStructs[i] = CreateObject(i); - - instances.Add(new Instance()); + instances.Add(CreateObject(i)); } } + private void CreateContinuesParticles(Timer arg1, UpdateContext arg2) + { + instances.Add(CreateObject(instances.Count)); + } + /// /// Creates a particle. /// - protected abstract ParticleStruct CreateObject(int index); + protected abstract ParticleInstance CreateObject(int index); /// /// Generates the desired matrix for drawing. /// - protected abstract Matrix4 CreateMatrix(ParticleStruct Struct, TDirection relativePosition); + protected abstract Matrix4 CreateMatrix(ParticleInstance Struct, TDirection relativePosition); } } \ No newline at end of file diff --git a/SMCode/SM.Base/Drawing/Particles/ParticleInstance.cs b/SMCode/SM.Base/Drawing/Particles/ParticleInstance.cs new file mode 100644 index 0000000..1010414 --- /dev/null +++ b/SMCode/SM.Base/Drawing/Particles/ParticleInstance.cs @@ -0,0 +1,38 @@ +using OpenTK; + +namespace SM.Base.Drawing.Particles +{ + public class ParticleInstance : Instance + { + public float StartLifetime = 0; + + /// + /// The lifetime this particular particle still has. + /// + public float Lifetime = 0; + + /// + /// A additional matrix to store rotation and scale. + /// + public Matrix4 Matrix; + + /// + /// Speeeeeeeeeed + /// + public float Speed; + } + + public class ParticleInstance : ParticleInstance + where TValue : struct + { + /// + /// A direction, that the particle should travel. + /// + public TValue Direction; + + /// + /// The start position. + /// + public TValue StartPosition; + } +} \ No newline at end of file diff --git a/SMCode/SM.Base/Drawing/Particles/ParticleMovement.cs b/SMCode/SM.Base/Drawing/Particles/ParticleMovement.cs index 6fdad6a..cccd773 100644 --- a/SMCode/SM.Base/Drawing/Particles/ParticleMovement.cs +++ b/SMCode/SM.Base/Drawing/Particles/ParticleMovement.cs @@ -14,17 +14,17 @@ namespace SM.Base.Drawing.Particles /// /// Default movement for 2D. /// - public static Vector2 Default2D(Vector2 direction, ParticleContext context) + public static Vector2 Default2D(ParticleInstance particle) { - return direction * (context.Timer.Elapsed * context.Speed); + return particle.Direction * ((particle.StartLifetime - particle.Lifetime) * particle.Speed); } /// /// Default movement for 3D. /// - public static Vector3 Default3D(Vector3 direction, ParticleContext context) + public static Vector3 Default3D(ParticleInstance particle) { - return direction * (context.Timer.Elapsed * context.Speed); + return particle.Direction * ((particle.StartLifetime - particle.Lifetime) * particle.Speed); } } } \ No newline at end of file diff --git a/SMCode/SM.Base/Drawing/Particles/ParticleStruct.cs b/SMCode/SM.Base/Drawing/Particles/ParticleStruct.cs deleted file mode 100644 index eef2b53..0000000 --- a/SMCode/SM.Base/Drawing/Particles/ParticleStruct.cs +++ /dev/null @@ -1,30 +0,0 @@ -#region usings - -using OpenTK; - -#endregion - -namespace SM.Base.Drawing.Particles -{ - /// - /// A particle... - /// - public struct ParticleStruct - where TDirection : struct - { - /// - /// A direction, that the particle should travel. - /// - public TDirection Direction; - - /// - /// A matrix to store rotation and scale. - /// - public Matrix4 Matrix; - - /// - /// Speeeeeeeeeed - /// - public float Speed; - } -} \ No newline at end of file diff --git a/SMCode/SM.Base/SM.Base.csproj b/SMCode/SM.Base/SM.Base.csproj index 5e201f9..1efa852 100644 --- a/SMCode/SM.Base/SM.Base.csproj +++ b/SMCode/SM.Base/SM.Base.csproj @@ -62,6 +62,7 @@ + @@ -69,9 +70,7 @@ - - diff --git a/SMCode/SM.Base/Time/Timer.cs b/SMCode/SM.Base/Time/Timer.cs index f6e3c37..15ff2bd 100644 --- a/SMCode/SM.Base/Time/Timer.cs +++ b/SMCode/SM.Base/Time/Timer.cs @@ -33,7 +33,7 @@ namespace SM.Base.Time /// /// The target time in seconds. /// - public float Target { get; } + public float Target { get; set; } /// /// The already elapsed time but normalized to the target. diff --git a/SMCode/SM2D/Drawing/DrawParticles.cs b/SMCode/SM2D/Drawing/DrawParticles.cs index 90f469a..c01bda2 100644 --- a/SMCode/SM2D/Drawing/DrawParticles.cs +++ b/SMCode/SM2D/Drawing/DrawParticles.cs @@ -12,7 +12,7 @@ namespace SM2D.Drawing public class DrawParticles : ParticleDrawingBasis { /// - public override Func MovementCalculation { get; set; } = ParticleMovement.Default2D; + public override Func, Vector2> MovementCalculation { get; set; } = ParticleMovement.Default2D; /// /// The direction the particles should travel. @@ -30,7 +30,7 @@ namespace SM2D.Drawing } /// - protected override ParticleStruct CreateObject(int index) + protected override ParticleInstance CreateObject(int index) { Vector2 dir; if (Direction.HasValue) @@ -42,18 +42,27 @@ namespace SM2D.Drawing } else dir = new Vector2(Randomize.GetFloat(-1, 1), Randomize.GetFloat(-1, 1)); - return new ParticleStruct() + var particle = new ParticleInstance() { - Matrix = Matrix4.CreateScale(1), + Matrix = DetachedParticles ? Transform.GetMatrix() : Matrix4.CreateScale(1), + Direction = dir, - Speed = Randomize.GetFloat(MaxSpeed) + StartPosition = Transform.Position, + + Speed = Randomize.GetFloat(MaxSpeed), + StartLifetime = Lifetime - Randomize.GetFloat(LifetimeRandomize) }; + particle.Lifetime = particle.StartLifetime; + + return particle; } /// - protected override Matrix4 CreateMatrix(ParticleStruct Struct, Vector2 direction) + protected override Matrix4 CreateMatrix(ParticleInstance Struct, Vector2 direction) { - return Struct.Matrix * Matrix4.CreateTranslation(direction.X, direction.Y, 0); + Vector2 pos = direction; + + return Struct.Matrix * Matrix4.CreateTranslation(pos.X, pos.Y, 0); } } } \ No newline at end of file diff --git a/SM_TEST/Program.cs b/SM_TEST/Program.cs index 940dcf7..2880498 100644 --- a/SM_TEST/Program.cs +++ b/SM_TEST/Program.cs @@ -12,6 +12,7 @@ using SM.Base.Drawing; using SM.Base.Time; using SM.Base.Window; using SM2D; +using SM2D.Controls; using SM2D.Drawing; using SM2D.Object; using SM2D.Scene; @@ -48,16 +49,17 @@ namespace SM_TEST }; - DrawObject2D test = new DrawObject2D() + particles = new DrawParticles(TimeSpan.FromSeconds(1)) { - Texture = new Bitmap("test.png") + Lifetime = 1f, + ContinuousInterval = .5f, + + Direction = -Vector2.UnitY, + DetachedParticles = true }; - test.Material.Blending = true; - test.Transform.Size.Set(100); - test.TextureTransform.SetRectangleRelative(test.Texture, new Vector2(234, 0), new Vector2(220, 201)); - test.Transform.AdjustSizeToTextureTransform(test.TextureTransform); + particles.Transform.Size.Set(50); - scene.Objects.Add(test); + scene.Objects.Add(particles); window.UpdateFrame += WindowOnUpdateFrame; window.RenderFrame += Window_RenderFrame; @@ -74,9 +76,11 @@ namespace SM_TEST private static void WindowOnUpdateFrame(object sender, FrameEventArgs e) { if (Mouse.LeftClick) - interpolation.Stop(); + particles.Trigger(); if (Mouse.RightClick) - interpolation.Stop(false); + particles.ContinuousInterval = .05f; + + particles.Transform.Position.Set(Mouse2D.InWorld(scene.Camera)); } } } \ No newline at end of file