Modifier.cs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. // Copyright (c) Craftwork Games. All rights reserved.
  2. // Licensed under the MIT license.
  3. // See LICENSE file in the project root for full license information.
  4. using System;
  5. namespace MonoGame.Extended.Particles.Modifiers;
  6. /// <summary>
  7. /// Represents a base class for all particle modifiers.
  8. /// </summary>
  9. /// <remarks>
  10. /// Particle modifiers are used to alter the behavior or properties of particles during their lifetime.
  11. /// Each modifier applies changes to particles at a configurable frequency, optimizing performance by
  12. /// spreading updates across frames when appropriate.
  13. /// Custom modifiers should inherit from this class and implement the <see cref="Update"/> method.
  14. /// </remarks>
  15. public abstract class Modifier
  16. {
  17. private const float DEFAULT_MODIFIER_FREQUENCY = 60.0f;
  18. private float _frequency;
  19. private float _cycleTime;
  20. private int _particlesUpdatedThisCycle;
  21. /// <summary>
  22. /// Gets or sets the display name of this modifier.
  23. /// </summary>
  24. public string Name;
  25. /// <summary>
  26. /// Gets or sets the update frequency of this modifier.
  27. /// </summary>
  28. /// <remarks>
  29. /// This value defines how often, in times per second, the modifier attempts to update
  30. /// the entire particle buffer. For example, a value of 60.0f means that all particles
  31. /// will be updated collectively approximately 60 times per second.
  32. ///
  33. /// To improve performance, updates are distributed across frames. Rather than updating
  34. /// every particle in every frame, the modifier mathematically distributes updates by
  35. /// processing a portion of the particles each frame based on the elapsed time and the
  36. /// desired frequency. Over time, this results in all particles being updated at the
  37. /// specified frequency on average, regardless of the actual frame rate.
  38. ///
  39. /// Higher values result in more frequent updates and smoother particle behavior, at the
  40. /// cost of performance. Lower values reduce CPU usage but may make particle changes appear
  41. /// less fluid.
  42. /// </remarks>
  43. public float Frequency
  44. {
  45. get => _frequency;
  46. set
  47. {
  48. if (value <= 0.0f)
  49. throw new ArgumentOutOfRangeException(nameof(value), "Frequency must be greater than zero.");
  50. _frequency = value;
  51. _cycleTime = 1f / _frequency;
  52. }
  53. }
  54. /// <summary>
  55. /// Indicates whether this modifier is enabled.
  56. /// </summary>
  57. /// <remarks>
  58. /// This value determines if this modifier is enabled. When a modifier is disabled, the modifier is not applied
  59. /// to the particles.
  60. /// </remarks>
  61. public bool Enabled;
  62. /// <summary>
  63. /// Initializes a new instance of the <see cref="Modifier"/> class.
  64. /// </summary>
  65. /// <remarks>
  66. /// The default constructor sets the <see cref="Name"/> property to the name of the derived class
  67. /// and initializes <see cref="Frequency"/> to <see cref="DEFAULT_MODIFIER_FREQUENCY"/>.
  68. /// </remarks>
  69. protected Modifier()
  70. {
  71. Name = GetType().Name;
  72. Frequency = DEFAULT_MODIFIER_FREQUENCY;
  73. Enabled = true;
  74. }
  75. internal void InternalUpdate(float elapsedSeconds, ParticleIterator iterator)
  76. {
  77. if (!Enabled || iterator.Total == 0)
  78. return;
  79. var particlesRemaining = iterator.Total - _particlesUpdatedThisCycle;
  80. var particlesToUpdate = Math.Min(particlesRemaining, (int)Math.Ceiling((elapsedSeconds / _cycleTime) * iterator.Total));
  81. if (particlesToUpdate > 0)
  82. {
  83. // Create a new iterator starting from the offset position
  84. var offsetIterator = iterator.Reset(_particlesUpdatedThisCycle);
  85. Update(_cycleTime, offsetIterator, particlesToUpdate);
  86. _particlesUpdatedThisCycle += particlesToUpdate;
  87. }
  88. if (_particlesUpdatedThisCycle >= iterator.Total)
  89. _particlesUpdatedThisCycle = 0;
  90. }
  91. /// <summary>
  92. /// Updates the properties of particles according to this modifier's specific behavior.
  93. /// </summary>
  94. /// <param name="elapsedSeconds">The elapsed time, in seconds, since the last update.</param>
  95. /// <param name="iterator">The iterator used to iterate the particles ot update.</param>
  96. protected internal abstract void Update(float elapsedSeconds, ParticleIterator iterator, int particleCount);
  97. }