ParticleSystem.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. //-----------------------------------------------------------------------------
  2. // ParticleSystem.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using System;
  8. using System.Xml.Serialization;
  9. using Microsoft.Xna.Framework;
  10. using Microsoft.Xna.Framework.Graphics;
  11. using Microsoft.Xna.Framework.Content;
  12. namespace NetRumble
  13. {
  14. /// <summary>
  15. /// A single-emitter particle system within a larger effect.
  16. /// </summary>
  17. public class ParticleSystem
  18. {
  19. /// <summary>
  20. /// The name of the particle system.
  21. /// </summary>
  22. private string name = "DefaultParticleSystem";
  23. /// <summary>
  24. /// The number of particles in this system.
  25. /// </summary>
  26. private int particleCount = 256;
  27. /// <summary>
  28. /// The current position of the system, relative to the effect.
  29. /// </summary>
  30. private Vector2 position = Vector2.Zero;
  31. /// <summary>
  32. /// The tint of the particles in the system.
  33. /// </summary>
  34. private Vector4 color =
  35. Microsoft.Xna.Framework.Color.White.ToVector4();
  36. /// <summary>
  37. /// The content name of the texture used with this system.
  38. /// </summary>
  39. private string textureName = "default_particle";
  40. /// <summary>
  41. /// The texture used with this system.
  42. /// </summary>
  43. private Texture2D texture = null;
  44. /// <summary>
  45. /// The center of the texture used with this system.
  46. /// </summary>
  47. /// <remarks>This is derived from the texture, calculated and stored.</remarks>
  48. private Vector2 textureOrigin;
  49. /// <summary>
  50. /// The blending mode for this system.
  51. /// </summary>
  52. private SpriteBlendMode blendMode = SpriteBlendMode.AlphaBlend;
  53. /// <summary>
  54. /// If true, the particle system is currently active.
  55. /// </summary>
  56. private bool active = false;
  57. [XmlIgnore()]
  58. public bool Active
  59. {
  60. get { return (active || (timeRemaining > 0.0f)); }
  61. }
  62. /// <summary>
  63. /// The amount of time that the particle system generates particles.
  64. /// </summary>
  65. private float duration = float.MaxValue;
  66. /// <summary>
  67. /// The time remaining for the particle system's generation.
  68. /// </summary>
  69. private float timeRemaining = float.MaxValue;
  70. /// <summary>
  71. /// The initial delay before the particle system starts generating particles.
  72. /// </summary>
  73. /// <remarks>Used for offsetting the start of particle systems.</remarks>
  74. private float initialDelay = 0f;
  75. /// <summary>
  76. /// The amount of time left on the initial delay.
  77. /// </summary>
  78. private float initialDelayRemaining = 0f;
  79. /// <summary>
  80. /// The number of particles that this system releases per second.
  81. /// </summary>
  82. private int particlesPerSecond = 128;
  83. /// <summary>
  84. /// The number of seconds between particle releases.
  85. /// </summary>
  86. /// <remarks>This is derived from particlesPerSecond.</remarks>
  87. private float releaseRate = 0.25f;
  88. /// <summary>
  89. /// The amount of time since the last particle was emitted.
  90. /// </summary>
  91. private float releaseTimer = 0f;
  92. /// <summary>
  93. /// The minimum lifetime possible for new particles.
  94. /// </summary>
  95. private float durationMinimum = 1f;
  96. /// <summary>
  97. /// The maximum lifetime possible for new particles.
  98. /// </summary>
  99. private float durationMaximum = 1f;
  100. /// <summary>
  101. /// The minimum initial velocity of new particles.
  102. /// </summary>
  103. private float velocityMinimum = 16f;
  104. /// <summary>
  105. /// The maximum initial velocity of new particles.
  106. /// </summary>
  107. private float velocityMaximum = 32f;
  108. /// <summary>
  109. /// The minimum acceleration of new particles.
  110. /// </summary>
  111. private float accelerationMinimum = 0f;
  112. /// <summary>
  113. /// The maximum acceleration of new particles.
  114. /// </summary>
  115. private float accelerationMaximum = 0f;
  116. /// <summary>
  117. /// The minimum initial scale of new particles.
  118. /// </summary>
  119. private float scaleMinimum = 1f;
  120. /// <summary>
  121. /// The maximum initial scale of new particles.
  122. /// </summary>
  123. private float scaleMaximum = 1f;
  124. /// <summary>
  125. /// The minimum initial opacity of new particles.
  126. /// </summary>
  127. private float opacityMinimum = 1f;
  128. /// <summary>
  129. /// The maximum initial opacity of new particles.
  130. /// </summary>
  131. private float opacityMaximum = 1f;
  132. /// <summary>
  133. /// The minimum release angle of new particles from the center.
  134. /// </summary>
  135. private float releaseAngleMinimum = 0f;
  136. /// <summary>
  137. /// The maximum release angle of new particles from the center.
  138. /// </summary>
  139. private float releaseAngleMaximum = 360f;
  140. /// <summary>
  141. /// The minimum release distance of new particles from the center.
  142. /// </summary>
  143. private float releaseDistanceMinimum = 0f;
  144. /// <summary>
  145. /// The maximum release distance of new particles from the center.
  146. /// </summary>
  147. private float releaseDistanceMaximum = 0f;
  148. /// <summary>
  149. /// The angular velocity of particles in this system.
  150. /// </summary>
  151. private float angularVelocity = 0f;
  152. /// <summary>
  153. /// The change in the scale per second for particles in this system.
  154. /// </summary>
  155. private float scaleDeltaPerSecond = 0f;
  156. /// <summary>
  157. /// The change in the opacity per second for particles in this system.
  158. /// </summary>
  159. private float opacityDeltaPerSecond = 0f;
  160. /// <summary>
  161. /// The cache of all particle objects in this system.
  162. /// </summary>
  163. private ParticleCache particles;
  164. /// <summary>
  165. /// Construct a new particle system.
  166. /// </summary>
  167. public ParticleSystem() { }
  168. /// <summary>
  169. /// Clone a particle system.
  170. /// </summary>
  171. /// <returns>A clone of this particle system.</returns>
  172. public ParticleSystem Clone()
  173. {
  174. ParticleSystem clone = new ParticleSystem();
  175. clone.name = this.name;
  176. clone.particleCount = this.particleCount;
  177. clone.position = this.position;
  178. clone.color = this.color;
  179. clone.textureName = this.textureName;
  180. clone.blendMode = this.blendMode;
  181. clone.duration = this.duration;
  182. clone.initialDelay = this.initialDelay;
  183. clone.particlesPerSecond = this.particlesPerSecond;
  184. clone.releaseRate = this.releaseRate;
  185. clone.durationMinimum = this.durationMinimum;
  186. clone.durationMaximum = this.durationMaximum;
  187. clone.velocityMinimum = this.velocityMinimum;
  188. clone.velocityMaximum = this.velocityMaximum;
  189. clone.accelerationMinimum = this.accelerationMinimum;
  190. clone.accelerationMaximum = this.accelerationMaximum;
  191. clone.scaleMinimum = this.scaleMinimum;
  192. clone.scaleMaximum = this.scaleMaximum;
  193. clone.opacityMinimum = this.opacityMinimum;
  194. clone.opacityMaximum = this.opacityMaximum;
  195. clone.releaseAngleMinimum = this.releaseAngleMinimum;
  196. clone.releaseAngleMaximum = this.releaseAngleMaximum;
  197. clone.releaseDistanceMinimum = this.releaseDistanceMinimum;
  198. clone.releaseDistanceMaximum = this.releaseDistanceMaximum;
  199. clone.angularVelocity = this.angularVelocity;
  200. clone.scaleDeltaPerSecond = this.scaleDeltaPerSecond;
  201. clone.opacityDeltaPerSecond = this.opacityDeltaPerSecond;
  202. return clone;
  203. }
  204. /// <summary>
  205. /// Initialize the particle system.
  206. /// </summary>
  207. /// <param name="content">The content manager that owns the texture.</param>
  208. public virtual void Initialize(ContentManager content)
  209. {
  210. // calculate the release rate
  211. releaseRate = 1.0f / (float)particlesPerSecond;
  212. // create the cache
  213. particles = new ParticleCache(particleCount);
  214. // load the texture
  215. try
  216. {
  217. texture = content.Load<Texture2D>(textureName);
  218. }
  219. catch (ContentLoadException)
  220. {
  221. texture = content.Load<Texture2D>("Textures/Particles/defaultParticle");
  222. }
  223. // calculate the origin on the texture
  224. textureOrigin = new Vector2(texture.Width / 2f, texture.Height / 2f);
  225. // allow us to start updating and drawing
  226. active = true;
  227. }
  228. /// <summary>
  229. /// Resets the particle system.
  230. /// </summary>
  231. public virtual void Reset()
  232. {
  233. // reset the cache
  234. particles.Reset();
  235. // reset the timers
  236. timeRemaining = duration;
  237. initialDelayRemaining = initialDelay;
  238. // allow us to start updating and drawing
  239. active = true;
  240. }
  241. /// <summary>
  242. /// Update the particle system.
  243. /// </summary>
  244. /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
  245. public virtual void Update(float elapsedTime)
  246. {
  247. // if the system isn't active, dont' update at all
  248. if (!Active)
  249. return;
  250. // update the initial delay
  251. if (initialDelayRemaining > 0.0f)
  252. {
  253. initialDelayRemaining -= elapsedTime;
  254. return;
  255. }
  256. // generate new particles
  257. GenerateParticles(elapsedTime);
  258. // update the existing particles
  259. UpdateParticles(elapsedTime);
  260. // update the active flag, based on if there are any used particles
  261. active = particles.UsedCount > 0;
  262. }
  263. /// <summary>
  264. /// Generate new particles into the system.
  265. /// </summary>
  266. /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
  267. private void GenerateParticles(float elapsedTime)
  268. {
  269. if (timeRemaining <= 0.0f)
  270. {
  271. return;
  272. }
  273. // update the timer
  274. timeRemaining -= elapsedTime;
  275. // release some particles if it's time
  276. releaseTimer += elapsedTime;
  277. while (releaseTimer >= releaseRate)
  278. {
  279. // only get new particles if you can
  280. Particle particle = particles.GetNextParticle();
  281. if (particle == null)
  282. {
  283. break;
  284. }
  285. else
  286. {
  287. // initialize the new particle
  288. InitializeParticle(particle);
  289. // reduce the release timer for the release rate of a particle
  290. releaseTimer -= releaseRate;
  291. }
  292. }
  293. }
  294. /// <summary>
  295. /// Update all of the individual particles in this system.
  296. /// </summary>
  297. /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
  298. private void UpdateParticles(float elapsedTime)
  299. {
  300. for (int i = 0; i < particles.Particles.Length; ++i)
  301. {
  302. if (particles.Particles[i].TimeRemaining > 0.0f)
  303. {
  304. // update the timer on the particle
  305. particles.Particles[i].TimeRemaining -= elapsedTime;
  306. // if the particle just timed out on this update,
  307. // add it to the freed-list.
  308. if (particles.Particles[i].TimeRemaining <= 0.0f)
  309. {
  310. particles.ReleaseParticle(particles.Particles[i]);
  311. continue;
  312. }
  313. // update the particle
  314. particles.Particles[i].Update(elapsedTime, angularVelocity,
  315. scaleDeltaPerSecond, opacityDeltaPerSecond);
  316. }
  317. }
  318. }
  319. /// <summary>
  320. /// Initialize a new particle using the values in this system.
  321. /// </summary>
  322. /// <param name="particle">The particle to be initialized.</param>
  323. private void InitializeParticle(Particle particle)
  324. {
  325. // safety-check the parameter
  326. if (particle == null)
  327. {
  328. throw new ArgumentNullException("particle");
  329. }
  330. // set the time remaining on the new particle
  331. particle.TimeRemaining = RandomMath.RandomBetween(durationMinimum,
  332. durationMaximum);
  333. // generate a random direction
  334. Vector2 direction = RandomMath.RandomDirection(releaseAngleMinimum,
  335. releaseAngleMaximum);
  336. // set the graphics data on the new particle
  337. particle.Position = position + direction *
  338. RandomMath.RandomBetween(releaseDistanceMinimum,
  339. releaseDistanceMaximum);
  340. particle.Velocity = direction * RandomMath.RandomBetween(velocityMinimum,
  341. velocityMaximum);
  342. if (particle.Velocity.LengthSquared() > 0f)
  343. {
  344. particle.Acceleration = direction *
  345. RandomMath.RandomBetween(accelerationMinimum, accelerationMaximum);
  346. }
  347. else
  348. {
  349. particle.Acceleration = Vector2.Zero;
  350. }
  351. particle.Rotation = RandomMath.RandomBetween(0f, MathHelper.TwoPi);
  352. particle.Scale = RandomMath.RandomBetween(scaleMinimum, scaleMaximum);
  353. particle.Opacity = RandomMath.RandomBetween(opacityMinimum, opacityMaximum);
  354. }
  355. /// <summary>
  356. /// Draw the particle system.
  357. /// </summary>
  358. /// <param name="spriteBatch">The SpriteBatch object used to draw.</param>
  359. public void Draw(SpriteBatch spriteBatch)
  360. {
  361. // only draw if we're active
  362. if (!Active)
  363. return;
  364. // draw each particle
  365. for (int p = 0; p < particles.Particles.Length; ++p)
  366. {
  367. Particle particle = particles.Particles[p];
  368. if (particle.TimeRemaining > 0.0f)
  369. {
  370. color.W = particle.Opacity;
  371. spriteBatch.Draw(texture, particle.Position, null, new Color(color),
  372. particle.Rotation, textureOrigin, particle.Scale,
  373. SpriteEffects.None, 1f);
  374. }
  375. }
  376. }
  377. /// <summary>
  378. /// Stop the particle system.
  379. /// </summary>
  380. /// <param name="immediately">
  381. /// If true, particles are no longer drawn or updated.
  382. /// Otherwise, only generation is halted.
  383. /// </param>
  384. public void Stop(bool immediately)
  385. {
  386. // halt generation
  387. timeRemaining = 0.0f;
  388. // halt updating/drawing of the particles if requested
  389. if (immediately)
  390. {
  391. active = false;
  392. }
  393. }
  394. public string Name
  395. {
  396. get { return name; }
  397. set { name = value; }
  398. }
  399. public int ParticleCount
  400. {
  401. get { return particleCount; }
  402. set { particleCount = value; }
  403. }
  404. public Vector2 Position
  405. {
  406. get { return position; }
  407. set { position = value; }
  408. }
  409. public Vector4 Color
  410. {
  411. get { return color; }
  412. set { color = value; }
  413. }
  414. public string TextureName
  415. {
  416. get { return textureName; }
  417. set { textureName = value; }
  418. }
  419. public SpriteBlendMode BlendMode
  420. {
  421. get { return blendMode; }
  422. set { blendMode = value; }
  423. }
  424. public float Duration
  425. {
  426. get { return duration; }
  427. set { duration = value; }
  428. }
  429. public float InitialDelay
  430. {
  431. get { return initialDelay; }
  432. set { initialDelay = value; }
  433. }
  434. public int ParticlesPerSecond
  435. {
  436. get { return particlesPerSecond; }
  437. set { particlesPerSecond = value; }
  438. }
  439. public float DurationMinimum
  440. {
  441. get { return durationMinimum; }
  442. set { durationMinimum = value; }
  443. }
  444. public float DurationMaximum
  445. {
  446. get { return durationMaximum; }
  447. set { durationMaximum = value; }
  448. }
  449. public float VelocityMinimum
  450. {
  451. get { return velocityMinimum; }
  452. set { velocityMinimum = value; }
  453. }
  454. public float VelocityMaximum
  455. {
  456. get { return velocityMaximum; }
  457. set { velocityMaximum = value; }
  458. }
  459. public float AccelerationMinimum
  460. {
  461. get { return accelerationMinimum; }
  462. set { accelerationMinimum = value; }
  463. }
  464. public float AccelerationMaximum
  465. {
  466. get { return accelerationMaximum; }
  467. set { accelerationMaximum = value; }
  468. }
  469. public float ScaleMinimum
  470. {
  471. get { return scaleMinimum; }
  472. set { scaleMinimum = value; }
  473. }
  474. public float ScaleMaximum
  475. {
  476. get { return scaleMaximum; }
  477. set { scaleMaximum = value; }
  478. }
  479. public float OpacityMinimum
  480. {
  481. get { return opacityMinimum; }
  482. set { opacityMinimum = value; }
  483. }
  484. public float OpacityMaximum
  485. {
  486. get { return opacityMaximum; }
  487. set { opacityMaximum = value; }
  488. }
  489. public float ReleaseAngleMinimum
  490. {
  491. get { return releaseAngleMinimum; }
  492. set { releaseAngleMinimum = value; }
  493. }
  494. public float ReleaseAngleMaximum
  495. {
  496. get { return releaseAngleMaximum; }
  497. set { releaseAngleMaximum = value; }
  498. }
  499. public float ReleaseDistanceMinimum
  500. {
  501. get { return releaseDistanceMinimum; }
  502. set { releaseDistanceMinimum = value; }
  503. }
  504. public float ReleaseDistanceMaximum
  505. {
  506. get { return releaseDistanceMaximum; }
  507. set { releaseDistanceMaximum = value; }
  508. }
  509. public float AngularVelocity
  510. {
  511. get { return angularVelocity; }
  512. set { angularVelocity = value; }
  513. }
  514. public float ScaleDeltaPerSecond
  515. {
  516. get { return scaleDeltaPerSecond; }
  517. set { scaleDeltaPerSecond = value; }
  518. }
  519. public float OpacityDeltaPerSecond
  520. {
  521. get { return opacityDeltaPerSecond; }
  522. set { opacityDeltaPerSecond = value; }
  523. }
  524. }
  525. }