123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712 |
- #region File Description
- //-----------------------------------------------------------------------------
- // ParticleSystem.cs
- //
- // Microsoft XNA Community Game Platform
- // Copyright (C) Microsoft Corporation. All rights reserved.
- //-----------------------------------------------------------------------------
- #endregion
- #region Using Statements
- using System;
- using System.Xml.Serialization;
- using Microsoft.Xna.Framework;
- using Microsoft.Xna.Framework.Graphics;
- using Microsoft.Xna.Framework.Content;
- #endregion
- namespace NetRumble
- {
- /// <summary>
- /// A single-emitter particle system within a larger effect.
- /// </summary>
- public class ParticleSystem
- {
- #region Description Data
- /// <summary>
- /// The name of the particle system.
- /// </summary>
- private string name = "DefaultParticleSystem";
- /// <summary>
- /// The number of particles in this system.
- /// </summary>
- private int particleCount = 256;
- #endregion
- #region Graphics Data
- /// <summary>
- /// The current position of the system, relative to the effect.
- /// </summary>
- private Vector2 position = Vector2.Zero;
- /// <summary>
- /// The tint of the particles in the system.
- /// </summary>
- private Vector4 color =
- Microsoft.Xna.Framework.Color.White.ToVector4();
- /// <summary>
- /// The content name of the texture used with this system.
- /// </summary>
- private string textureName = "default_particle";
- /// <summary>
- /// The texture used with this system.
- /// </summary>
- private Texture2D texture = null;
- /// <summary>
- /// The center of the texture used with this system.
- /// </summary>
- /// <remarks>This is derived from the texture, calculated and stored.</remarks>
- private Vector2 textureOrigin;
- /// <summary>
- /// The blending mode for this system.
- /// </summary>
- private SpriteBlendMode blendMode = SpriteBlendMode.AlphaBlend;
- #endregion
- #region Status Data
- /// <summary>
- /// If true, the particle system is currently active.
- /// </summary>
- private bool active = false;
- [XmlIgnore()]
- public bool Active
- {
- get { return (active || (timeRemaining > 0.0f)); }
- }
- /// <summary>
- /// The amount of time that the particle system generates particles.
- /// </summary>
- private float duration = float.MaxValue;
- /// <summary>
- /// The time remaining for the particle system's generation.
- /// </summary>
- private float timeRemaining = float.MaxValue;
- /// <summary>
- /// The initial delay before the particle system starts generating particles.
- /// </summary>
- /// <remarks>Used for offsetting the start of particle systems.</remarks>
- private float initialDelay = 0f;
- /// <summary>
- /// The amount of time left on the initial delay.
- /// </summary>
- private float initialDelayRemaining = 0f;
- #endregion
- #region Particle Creation Data
- /// <summary>
- /// The number of particles that this system releases per second.
- /// </summary>
- private int particlesPerSecond = 128;
- /// <summary>
- /// The number of seconds between particle releases.
- /// </summary>
- /// <remarks>This is derived from particlesPerSecond.</remarks>
- private float releaseRate = 0.25f;
- /// <summary>
- /// The amount of time since the last particle was emitted.
- /// </summary>
- private float releaseTimer = 0f;
-
- /// <summary>
- /// The minimum lifetime possible for new particles.
- /// </summary>
- private float durationMinimum = 1f;
- /// <summary>
- /// The maximum lifetime possible for new particles.
- /// </summary>
- private float durationMaximum = 1f;
-
- /// <summary>
- /// The minimum initial velocity of new particles.
- /// </summary>
- private float velocityMinimum = 16f;
- /// <summary>
- /// The maximum initial velocity of new particles.
- /// </summary>
- private float velocityMaximum = 32f;
- /// <summary>
- /// The minimum acceleration of new particles.
- /// </summary>
- private float accelerationMinimum = 0f;
- /// <summary>
- /// The maximum acceleration of new particles.
- /// </summary>
- private float accelerationMaximum = 0f;
- /// <summary>
- /// The minimum initial scale of new particles.
- /// </summary>
- private float scaleMinimum = 1f;
- /// <summary>
- /// The maximum initial scale of new particles.
- /// </summary>
- private float scaleMaximum = 1f;
- /// <summary>
- /// The minimum initial opacity of new particles.
- /// </summary>
- private float opacityMinimum = 1f;
- /// <summary>
- /// The maximum initial opacity of new particles.
- /// </summary>
- private float opacityMaximum = 1f;
- /// <summary>
- /// The minimum release angle of new particles from the center.
- /// </summary>
- private float releaseAngleMinimum = 0f;
- /// <summary>
- /// The maximum release angle of new particles from the center.
- /// </summary>
- private float releaseAngleMaximum = 360f;
- /// <summary>
- /// The minimum release distance of new particles from the center.
- /// </summary>
- private float releaseDistanceMinimum = 0f;
- /// <summary>
- /// The maximum release distance of new particles from the center.
- /// </summary>
- private float releaseDistanceMaximum = 0f;
- #endregion
- #region Particle Updating Data
- /// <summary>
- /// The angular velocity of particles in this system.
- /// </summary>
- private float angularVelocity = 0f;
- /// <summary>
- /// The change in the scale per second for particles in this system.
- /// </summary>
- private float scaleDeltaPerSecond = 0f;
- /// <summary>
- /// The change in the opacity per second for particles in this system.
- /// </summary>
- private float opacityDeltaPerSecond = 0f;
- #endregion
- #region Particle Cache
- /// <summary>
- /// The cache of all particle objects in this system.
- /// </summary>
- private ParticleCache particles;
- #endregion
- #region Initialization Methods
- /// <summary>
- /// Construct a new particle system.
- /// </summary>
- public ParticleSystem() { }
- /// <summary>
- /// Clone a particle system.
- /// </summary>
- /// <returns>A clone of this particle system.</returns>
- public ParticleSystem Clone()
- {
- ParticleSystem clone = new ParticleSystem();
- clone.name = this.name;
- clone.particleCount = this.particleCount;
- clone.position = this.position;
- clone.color = this.color;
- clone.textureName = this.textureName;
- clone.blendMode = this.blendMode;
- clone.duration = this.duration;
- clone.initialDelay = this.initialDelay;
- clone.particlesPerSecond = this.particlesPerSecond;
- clone.releaseRate = this.releaseRate;
- clone.durationMinimum = this.durationMinimum;
- clone.durationMaximum = this.durationMaximum;
- clone.velocityMinimum = this.velocityMinimum;
- clone.velocityMaximum = this.velocityMaximum;
- clone.accelerationMinimum = this.accelerationMinimum;
- clone.accelerationMaximum = this.accelerationMaximum;
- clone.scaleMinimum = this.scaleMinimum;
- clone.scaleMaximum = this.scaleMaximum;
- clone.opacityMinimum = this.opacityMinimum;
- clone.opacityMaximum = this.opacityMaximum;
- clone.releaseAngleMinimum = this.releaseAngleMinimum;
- clone.releaseAngleMaximum = this.releaseAngleMaximum;
- clone.releaseDistanceMinimum = this.releaseDistanceMinimum;
- clone.releaseDistanceMaximum = this.releaseDistanceMaximum;
- clone.angularVelocity = this.angularVelocity;
- clone.scaleDeltaPerSecond = this.scaleDeltaPerSecond;
- clone.opacityDeltaPerSecond = this.opacityDeltaPerSecond;
- return clone;
- }
- /// <summary>
- /// Initialize the particle system.
- /// </summary>
- /// <param name="content">The content manager that owns the texture.</param>
- public virtual void Initialize(ContentManager content)
- {
- // calculate the release rate
- releaseRate = 1.0f / (float)particlesPerSecond;
- // create the cache
- particles = new ParticleCache(particleCount);
- // load the texture
- try
- {
- texture = content.Load<Texture2D>(textureName);
- }
- catch (ContentLoadException)
- {
- texture = content.Load<Texture2D>("Textures/Particles/defaultParticle");
- }
- // calculate the origin on the texture
- textureOrigin = new Vector2(texture.Width / 2f, texture.Height / 2f);
-
- // allow us to start updating and drawing
- active = true;
- }
- /// <summary>
- /// Resets the particle system.
- /// </summary>
- public virtual void Reset()
- {
- // reset the cache
- particles.Reset();
- // reset the timers
- timeRemaining = duration;
- initialDelayRemaining = initialDelay;
- // allow us to start updating and drawing
- active = true;
- }
- #endregion
- #region Updating Methods
- /// <summary>
- /// Update the particle system.
- /// </summary>
- /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
- public virtual void Update(float elapsedTime)
- {
- // if the system isn't active, dont' update at all
- if (!Active)
- return;
- // update the initial delay
- if (initialDelayRemaining > 0.0f)
- {
- initialDelayRemaining -= elapsedTime;
- return;
- }
-
- // generate new particles
- GenerateParticles(elapsedTime);
- // update the existing particles
- UpdateParticles(elapsedTime);
- // update the active flag, based on if there are any used particles
- active = particles.UsedCount > 0;
- }
- /// <summary>
- /// Generate new particles into the system.
- /// </summary>
- /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
- private void GenerateParticles(float elapsedTime)
- {
- if (timeRemaining <= 0.0f)
- {
- return;
- }
- // update the timer
- timeRemaining -= elapsedTime;
- // release some particles if it's time
- releaseTimer += elapsedTime;
- while (releaseTimer >= releaseRate)
- {
- // only get new particles if you can
- Particle particle = particles.GetNextParticle();
- if (particle == null)
- {
- break;
- }
- else
- {
- // initialize the new particle
- InitializeParticle(particle);
- // reduce the release timer for the release rate of a particle
- releaseTimer -= releaseRate;
- }
- }
- }
- /// <summary>
- /// Update all of the individual particles in this system.
- /// </summary>
- /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
- private void UpdateParticles(float elapsedTime)
- {
- for (int i = 0; i < particles.Particles.Length; ++i)
- {
- if (particles.Particles[i].TimeRemaining > 0.0f)
- {
- // update the timer on the particle
- particles.Particles[i].TimeRemaining -= elapsedTime;
- // if the particle just timed out on this update,
- // add it to the freed-list.
- if (particles.Particles[i].TimeRemaining <= 0.0f)
- {
- particles.ReleaseParticle(particles.Particles[i]);
- continue;
- }
- // update the particle
- particles.Particles[i].Update(elapsedTime, angularVelocity,
- scaleDeltaPerSecond, opacityDeltaPerSecond);
- }
- }
- }
- /// <summary>
- /// Initialize a new particle using the values in this system.
- /// </summary>
- /// <param name="particle">The particle to be initialized.</param>
- private void InitializeParticle(Particle particle)
- {
- // safety-check the parameter
- if (particle == null)
- {
- throw new ArgumentNullException("particle");
- }
- // set the time remaining on the new particle
- particle.TimeRemaining = RandomMath.RandomBetween(durationMinimum,
- durationMaximum);
- // generate a random direction
- Vector2 direction = RandomMath.RandomDirection(releaseAngleMinimum,
- releaseAngleMaximum);
- // set the graphics data on the new particle
- particle.Position = position + direction *
- RandomMath.RandomBetween(releaseDistanceMinimum,
- releaseDistanceMaximum);
- particle.Velocity = direction * RandomMath.RandomBetween(velocityMinimum,
- velocityMaximum);
- if (particle.Velocity.LengthSquared() > 0f)
- {
- particle.Acceleration = direction *
- RandomMath.RandomBetween(accelerationMinimum, accelerationMaximum);
- }
- else
- {
- particle.Acceleration = Vector2.Zero;
- }
- particle.Rotation = RandomMath.RandomBetween(0f, MathHelper.TwoPi);
- particle.Scale = RandomMath.RandomBetween(scaleMinimum, scaleMaximum);
- particle.Opacity = RandomMath.RandomBetween(opacityMinimum, opacityMaximum);
- }
- #endregion
- #region Drawing Methods
- /// <summary>
- /// Draw the particle system.
- /// </summary>
- /// <param name="spriteBatch">The SpriteBatch object used to draw.</param>
- public void Draw(SpriteBatch spriteBatch)
- {
- // only draw if we're active
- if (!Active)
- return;
- // draw each particle
- for (int p = 0; p < particles.Particles.Length; ++p)
- {
- Particle particle = particles.Particles[p];
- if (particle.TimeRemaining > 0.0f)
- {
- color.W = particle.Opacity;
- spriteBatch.Draw(texture, particle.Position, null, new Color(color),
- particle.Rotation, textureOrigin, particle.Scale,
- SpriteEffects.None, 1f);
- }
- }
- }
- #endregion
- #region Control Methods
- /// <summary>
- /// Stop the particle system.
- /// </summary>
- /// <param name="immediately">
- /// If true, particles are no longer drawn or updated.
- /// Otherwise, only generation is halted.
- /// </param>
- public void Stop(bool immediately)
- {
- // halt generation
- timeRemaining = 0.0f;
- // halt updating/drawing of the particles if requested
- if (immediately)
- {
- active = false;
- }
- }
- #endregion
- #region Serialization Interface
- public string Name
- {
- get { return name; }
- set { name = value; }
- }
- public int ParticleCount
- {
- get { return particleCount; }
- set { particleCount = value; }
- }
- public Vector2 Position
- {
- get { return position; }
- set { position = value; }
- }
- public Vector4 Color
- {
- get { return color; }
- set { color = value; }
- }
- public string TextureName
- {
- get { return textureName; }
- set { textureName = value; }
- }
- public SpriteBlendMode BlendMode
- {
- get { return blendMode; }
- set { blendMode = value; }
- }
-
- public float Duration
- {
- get { return duration; }
- set { duration = value; }
- }
- public float InitialDelay
- {
- get { return initialDelay; }
- set { initialDelay = value; }
- }
- public int ParticlesPerSecond
- {
- get { return particlesPerSecond; }
- set { particlesPerSecond = value; }
- }
- public float DurationMinimum
- {
- get { return durationMinimum; }
- set { durationMinimum = value; }
- }
- public float DurationMaximum
- {
- get { return durationMaximum; }
- set { durationMaximum = value; }
- }
- public float VelocityMinimum
- {
- get { return velocityMinimum; }
- set { velocityMinimum = value; }
- }
- public float VelocityMaximum
- {
- get { return velocityMaximum; }
- set { velocityMaximum = value; }
- }
- public float AccelerationMinimum
- {
- get { return accelerationMinimum; }
- set { accelerationMinimum = value; }
- }
- public float AccelerationMaximum
- {
- get { return accelerationMaximum; }
- set { accelerationMaximum = value; }
- }
- public float ScaleMinimum
- {
- get { return scaleMinimum; }
- set { scaleMinimum = value; }
- }
- public float ScaleMaximum
- {
- get { return scaleMaximum; }
- set { scaleMaximum = value; }
- }
- public float OpacityMinimum
- {
- get { return opacityMinimum; }
- set { opacityMinimum = value; }
- }
- public float OpacityMaximum
- {
- get { return opacityMaximum; }
- set { opacityMaximum = value; }
- }
- public float ReleaseAngleMinimum
- {
- get { return releaseAngleMinimum; }
- set { releaseAngleMinimum = value; }
- }
- public float ReleaseAngleMaximum
- {
- get { return releaseAngleMaximum; }
- set { releaseAngleMaximum = value; }
- }
- public float ReleaseDistanceMinimum
- {
- get { return releaseDistanceMinimum; }
- set { releaseDistanceMinimum = value; }
- }
- public float ReleaseDistanceMaximum
- {
- get { return releaseDistanceMaximum; }
- set { releaseDistanceMaximum = value; }
- }
- public float AngularVelocity
- {
- get { return angularVelocity; }
- set { angularVelocity = value; }
- }
-
- public float ScaleDeltaPerSecond
- {
- get { return scaleDeltaPerSecond; }
- set { scaleDeltaPerSecond = value; }
- }
- public float OpacityDeltaPerSecond
- {
- get { return opacityDeltaPerSecond; }
- set { opacityDeltaPerSecond = value; }
- }
- #endregion
- }
- }
|