ParticleDemo.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. //-----------------------------------------------------------------------------
  2. // ParticleDemo.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using System.Text;
  11. using Microsoft.Xna.Framework;
  12. using Microsoft.Xna.Framework.Graphics;
  13. using SkinnedModel;
  14. namespace XnaGraphicsDemo
  15. {
  16. /// <summary>
  17. /// Demo shows how to use SpriteBatch.
  18. /// </summary>
  19. class ParticleDemo : MenuComponent
  20. {
  21. const int MaxParticles = 5000;
  22. /// <summary>
  23. /// Represents a single particle in the particle system.
  24. /// </summary>
  25. struct Particle
  26. {
  27. public Vector2 Position;
  28. public Vector2 Velocity;
  29. public float Size;
  30. public float Rotation;
  31. public float Spin;
  32. public Color Color;
  33. }
  34. Particle[] particles = new Particle[MaxParticles];
  35. int firstParticle;
  36. int particleCount;
  37. FloatMenuEntry spawnRate;
  38. float spawnCounter;
  39. Texture2D cat;
  40. Random random = new Random();
  41. /// <summary>
  42. /// Initializes a new instance of the <see cref="ParticleDemo"/> class and sets up menu entries.
  43. /// </summary>
  44. /// <param name="game">The game instance.</param>
  45. public ParticleDemo(DemoGame game)
  46. : base(game)
  47. {
  48. Entries.Add(spawnRate = new FloatMenuEntry() { Text = "spawn rate" });
  49. // This menu option for changing the resolution is currently disabled,
  50. // because the image scaler feature is not yet implemented in the CTP release.
  51. /*
  52. Entries.Add(new ResolutionMenu(game.graphics));
  53. */
  54. Entries.Add(new MenuEntry
  55. {
  56. Text = "back",
  57. Clicked = delegate
  58. {
  59. // Before we quit back out of this menu, reset back to the default resolution.
  60. if (game.Graphics.PreferredBackBufferWidth != 480)
  61. {
  62. game.Graphics.PreferredBackBufferWidth = 480;
  63. game.Graphics.PreferredBackBufferHeight = 800;
  64. game.Graphics.ApplyChanges();
  65. }
  66. Game.SetActiveMenu(0);
  67. } });
  68. }
  69. /// <summary>
  70. /// Resets the menu state and particle system to defaults.
  71. /// </summary>
  72. public override void Reset()
  73. {
  74. firstParticle = 0;
  75. particleCount = 0;
  76. spawnRate.Value = 0.2f;
  77. base.Reset();
  78. }
  79. /// <summary>
  80. /// Loads content for this demo, including the cat texture.
  81. /// </summary>
  82. protected override void LoadContent()
  83. {
  84. cat = Game.Content.Load<Texture2D>("cat");
  85. }
  86. /// <summary>
  87. /// Updates the particle system and spawns new particles as needed.
  88. /// </summary>
  89. /// <param name="gameTime">The current game time.</param>
  90. public override void Update(GameTime gameTime)
  91. {
  92. int i = firstParticle;
  93. for (int j = particleCount; j > 0; j--)
  94. {
  95. // Move a particle.
  96. particles[i].Position += particles[i].Velocity;
  97. particles[i].Rotation += particles[i].Spin;
  98. particles[i].Velocity.Y += 0.1f;
  99. // Retire old particles?
  100. const float borderPadding = 96;
  101. if (i == firstParticle)
  102. {
  103. if ((particles[i].Position.X < -borderPadding) ||
  104. (particles[i].Position.X > 480 + borderPadding) ||
  105. (particles[i].Position.Y < -borderPadding) ||
  106. (particles[i].Position.Y > 800 + borderPadding))
  107. {
  108. if (++firstParticle >= MaxParticles)
  109. firstParticle = 0;
  110. particleCount--;
  111. }
  112. }
  113. if (++i >= MaxParticles)
  114. i = 0;
  115. }
  116. // Spawn new particles?
  117. spawnCounter += spawnRate.Value * 10;
  118. while (spawnCounter > 1)
  119. {
  120. SpawnParticle(null);
  121. spawnCounter--;
  122. }
  123. base.Update(gameTime);
  124. }
  125. /// <summary>
  126. /// Helper method to create a new cat particle at the given position, or at a random position if null.
  127. /// </summary>
  128. /// <param name="position">The position to spawn the particle, or null for random.</param>
  129. void SpawnParticle(Vector2? position)
  130. /// <returns>Nothing. Spawns a new particle if possible.</returns>
  131. {
  132. if (particleCount >= MaxParticles)
  133. return;
  134. int i = firstParticle + particleCount;
  135. if (i >= MaxParticles)
  136. i -= MaxParticles;
  137. particles[i].Position = position ?? new Vector2((float)random.NextDouble() * 480, (float)random.NextDouble() * 800);
  138. particles[i].Velocity = new Vector2((float)random.NextDouble() - 0.5f, (float)random.NextDouble() - 0.5f) * 10f;
  139. particles[i].Size = (float)random.NextDouble() * 0.5f + 0.5f;
  140. particles[i].Rotation = 0;
  141. particles[i].Spin = ((float)random.NextDouble() - 0.5f) * 0.1f;
  142. if (position.HasValue)
  143. {
  144. // Explicitly positioned particles have no tint.
  145. particles[i].Color = Color.White;
  146. }
  147. else
  148. {
  149. // Randomly positioned particles have random tint colors.
  150. byte r = (byte)(128 + random.NextDouble() * 127);
  151. byte g = (byte)(128 + random.NextDouble() * 127);
  152. byte b = (byte)(128 + random.NextDouble() * 127);
  153. particles[i].Color = new Color(r, g, b);
  154. }
  155. particleCount++;
  156. }
  157. /// <summary>
  158. /// Draws the cat particle system and menu.
  159. /// </summary>
  160. /// <param name="gameTime">The current game time.</param>
  161. public override void Draw(GameTime gameTime)
  162. {
  163. DrawTitle("particles", Color.CornflowerBlue, Color.Lerp(Color.Blue, Color.CornflowerBlue, 0.85f));
  164. SpriteBatch.Begin(0, null, null, null, null, null, Game.ScaleMatrix);
  165. Vector2 origin = new Vector2(cat.Width, cat.Height) / 2;
  166. int i = firstParticle + particleCount - 1;
  167. if (i >= MaxParticles)
  168. i -= MaxParticles;
  169. for (int j = 0; j < particleCount; j++)
  170. {
  171. SpriteBatch.Draw(cat, particles[i].Position, null, particles[i].Color, particles[i].Rotation, origin, particles[i].Size, 0, 0);
  172. if (--i < 0)
  173. i = MaxParticles - 1;
  174. }
  175. SpriteBatch.End();
  176. base.Draw(gameTime);
  177. }
  178. /// <summary>
  179. /// Dragging on the menu background creates new particles at the last touch point.
  180. /// </summary>
  181. /// <param name="delta">The amount the pointer has moved since the last drag event.</param>
  182. protected override void OnDrag(Vector2 delta)
  183. {
  184. SpawnParticle(LastTouchPoint);
  185. }
  186. /// <summary>
  187. /// Custom menu entry subclass for cycling through different backbuffer resolutions.
  188. /// </summary>
  189. class ResolutionMenu : MenuEntry
  190. {
  191. GraphicsDeviceManager graphics;
  192. /// <summary>
  193. /// Initializes a new instance of the <see cref="ResolutionMenu"/> class for changing resolutions.
  194. /// </summary>
  195. /// <param name="graphics">The graphics device manager to modify.</param>
  196. public ResolutionMenu(GraphicsDeviceManager graphics)
  197. {
  198. this.graphics = graphics;
  199. }
  200. /// <summary>
  201. /// Gets the display text for the current resolution.
  202. /// </summary>
  203. public override string Text
  204. {
  205. get { return string.Format("{0}x{1}", graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight); }
  206. set { }
  207. }
  208. /// <summary>
  209. /// Cycles through available resolutions and applies the change.
  210. /// </summary>
  211. public override void OnClicked()
  212. {
  213. switch (graphics.PreferredBackBufferWidth)
  214. {
  215. case 480:
  216. graphics.PreferredBackBufferWidth = 360;
  217. graphics.PreferredBackBufferHeight = 600;
  218. break;
  219. case 360:
  220. graphics.PreferredBackBufferWidth = 240;
  221. graphics.PreferredBackBufferHeight = 400;
  222. break;
  223. case 240:
  224. graphics.PreferredBackBufferWidth = 480;
  225. graphics.PreferredBackBufferHeight = 800;
  226. break;
  227. }
  228. graphics.ApplyChanges();
  229. base.OnClicked();
  230. }
  231. }
  232. }
  233. }