ParticleSampleGame.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. //-----------------------------------------------------------------------------
  2. // ParticleSampleGame.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.Text;
  10. using Microsoft.Xna.Framework;
  11. using Microsoft.Xna.Framework.Graphics;
  12. using Microsoft.Xna.Framework.Content;
  13. using Microsoft.Xna.Framework.Input;
  14. using Microsoft.Xna.Framework.Input.Touch;
  15. namespace ParticleSample
  16. {
  17. /// <summary>
  18. /// This is the main type for the ParticleSample, and inherits from the Framework's
  19. /// Game class. It creates three different kinds of ParticleSystems, and then adds
  20. /// them to its components collection. It also has keeps a random number generator,
  21. /// a SpriteBatch, and a ContentManager that the different classes in this sample
  22. /// can share.
  23. /// </summary>
  24. public class ParticleSampleGame : Microsoft.Xna.Framework.Game
  25. {
  26. GraphicsDeviceManager graphics;
  27. // The particle systems will all need a SpriteBatch to draw their particles,
  28. // so let's make one they can share. We'll use this to draw our SpriteFont
  29. // too.
  30. SpriteBatch spriteBatch;
  31. public SpriteBatch SpriteBatch
  32. {
  33. get { return spriteBatch; }
  34. }
  35. // Used to draw the instructions on the screen.
  36. SpriteFont font;
  37. // a random number generator that the whole sample can share.
  38. private static Random random = new Random();
  39. public static Random Random
  40. {
  41. get { return random; }
  42. }
  43. // Here's the really fun part of the sample, the particle systems! These are
  44. // drawable game components, so we can just add them to the components
  45. // collection. Read more about each particle system in their respective source
  46. // files.
  47. ExplosionParticleSystem explosion;
  48. ExplosionSmokeParticleSystem smoke;
  49. SmokePlumeParticleSystem smokePlume;
  50. // State is an enum that represents which effect we're currently demoing.
  51. enum State
  52. {
  53. Explosions,
  54. SmokePlume
  55. };
  56. // the number of values in the "State" enum.
  57. const int NumStates = 2;
  58. State currentState = State.Explosions;
  59. // a timer that will tell us when it's time to trigger another explosion.
  60. const float TimeBetweenExplosions = 2.0f;
  61. float timeTillExplosion = 0.0f;
  62. // keep a timer that will tell us when it's time to add more particles to the
  63. // smoke plume.
  64. const float TimeBetweenSmokePlumePuffs = .5f;
  65. float timeTillPuff = 0.0f;
  66. // keep track of the last frame's keyboard and gamepad state, so that we know
  67. // if the user has pressed a button.
  68. KeyboardState lastKeyboardState;
  69. GamePadState lastGamepadState;
  70. public ParticleSampleGame()
  71. {
  72. graphics = new GraphicsDeviceManager(this);
  73. #if WINDOWS_PHONE
  74. graphics.IsFullScreen = false;
  75. // Frame rate is 30 fps by default for Windows Phone.
  76. TargetElapsedTime = TimeSpan.FromTicks(333333);
  77. #endif
  78. Content.RootDirectory = "Content";
  79. // create the particle systems and add them to the components list.
  80. // we should never see more than one explosion at once
  81. explosion = new ExplosionParticleSystem(this, 1);
  82. Components.Add(explosion);
  83. // but the smoke from the explosion lingers a while.
  84. smoke = new ExplosionSmokeParticleSystem(this, 2);
  85. Components.Add(smoke);
  86. // we'll see lots of these effects at once; this is ok
  87. // because they have a fairly small number of particles per effect.
  88. smokePlume = new SmokePlumeParticleSystem(this, 9);
  89. Components.Add(smokePlume);
  90. // enable the tap gesture for changing particle effects
  91. TouchPanel.EnabledGestures = GestureType.Tap;
  92. }
  93. /// <summary>
  94. /// Load your graphics content.
  95. /// </summary>
  96. protected override void LoadContent()
  97. {
  98. spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
  99. font = Content.Load<SpriteFont>("font");
  100. }
  101. /// <summary>
  102. /// Allows the game to run logic such as updating the world,
  103. /// checking for collisions, gathering input and playing audio.
  104. /// </summary>
  105. /// <param name="gameTime">Provides a snapshot of timing values.</param>
  106. protected override void Update(GameTime gameTime)
  107. {
  108. // check the input devices to see if someone has decided they want to see
  109. // the other effect, if they want to quit.
  110. HandleInput();
  111. float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
  112. switch (currentState)
  113. {
  114. // if we should be demoing the explosions effect, check to see if it's
  115. // time for a new explosion.
  116. case State.Explosions:
  117. UpdateExplosions(dt);
  118. break;
  119. // if we're showing off the smoke plume, check to see if it's time for a
  120. // new puff of smoke.
  121. case State.SmokePlume:
  122. UpdateSmokePlume(dt);
  123. break;
  124. }
  125. // the base update will handle updating the particle systems themselves,
  126. // because we added them to the components collection.
  127. base.Update(gameTime);
  128. }
  129. // this function is called when we want to demo the smoke plume effect. it
  130. // updates the timeTillPuff timer, and adds more particles to the plume when
  131. // necessary.
  132. private void UpdateSmokePlume(float dt)
  133. {
  134. timeTillPuff -= dt;
  135. if (timeTillPuff < 0)
  136. {
  137. Vector2 where = Vector2.Zero;
  138. // add more particles at the bottom of the screen, halfway across.
  139. where.X = graphics.GraphicsDevice.Viewport.Width / 2;
  140. where.Y = graphics.GraphicsDevice.Viewport.Height;
  141. smokePlume.AddParticles(where);
  142. // and then reset the timer.
  143. timeTillPuff = TimeBetweenSmokePlumePuffs;
  144. }
  145. }
  146. // this function is called when we want to demo the explosion effect. it
  147. // updates the timeTillExplosion timer, and starts another explosion effect
  148. // when the timer reaches zero.
  149. private void UpdateExplosions(float dt)
  150. {
  151. timeTillExplosion -= dt;
  152. if (timeTillExplosion < 0)
  153. {
  154. Vector2 where = Vector2.Zero;
  155. // create the explosion at some random point on the screen.
  156. where.X = RandomBetween(0, graphics.GraphicsDevice.Viewport.Width);
  157. where.Y = RandomBetween(0, graphics.GraphicsDevice.Viewport.Height);
  158. // the overall explosion effect is actually comprised of two particle
  159. // systems: the fiery bit, and the smoke behind it. add particles to
  160. // both of those systems.
  161. explosion.AddParticles(where);
  162. smoke.AddParticles(where);
  163. // reset the timer.
  164. timeTillExplosion = TimeBetweenExplosions;
  165. }
  166. }
  167. /// <summary>
  168. /// This is called when the game should draw itself.
  169. /// </summary>
  170. /// <param name="gameTime">Provides a snapshot of timing values.</param>
  171. protected override void Draw(GameTime gameTime)
  172. {
  173. graphics.GraphicsDevice.Clear(Color.Black);
  174. spriteBatch.Begin();
  175. // draw some instructions on the screen
  176. string message = string.Format("Current effect: {0}!\n" +
  177. "Hit the A button or space bar, or tap the screen, to switch.\n" +
  178. "Hit the F key to toggle full screen.\n\n" +
  179. "Free particles:\n" +
  180. " ExplosionParticleSystem: {1}\n" +
  181. " ExplosionSmokeParticleSystem: {2}\n" +
  182. " SmokePlumeParticleSystem: {3}",
  183. currentState, explosion.FreeParticleCount,
  184. smoke.FreeParticleCount, smokePlume.FreeParticleCount );
  185. spriteBatch.DrawString(font, message, new Vector2(50, 50), Color.White);
  186. spriteBatch.End();
  187. base.Draw(gameTime);
  188. }
  189. // This function will check to see if the user has just pushed the A button or
  190. // the space bar. If so, we should go to the next effect.
  191. private void HandleInput()
  192. {
  193. KeyboardState currentKeyboardState = Keyboard.GetState();
  194. GamePadState currentGamePadState = GamePad.GetState(PlayerIndex.One);
  195. // Allows the game to exit
  196. if (currentGamePadState.Buttons.Back == ButtonState.Pressed ||
  197. currentKeyboardState.IsKeyDown(Keys.Escape))
  198. this.Exit();
  199. // check to see if someone has just released the space bar.
  200. bool keyboardSpace =
  201. currentKeyboardState.IsKeyUp(Keys.Space) &&
  202. lastKeyboardState.IsKeyDown(Keys.Space);
  203. // check to see if someone has just released the 'F' key.
  204. bool keyboardF =
  205. currentKeyboardState.IsKeyUp(Keys.F) &&
  206. lastKeyboardState.IsKeyDown(Keys.F);
  207. // check the gamepad to see if someone has just released the A button.
  208. bool gamepadA =
  209. currentGamePadState.Buttons.A == ButtonState.Pressed &&
  210. lastGamepadState.Buttons.A == ButtonState.Released;
  211. // check our gestures to see if someone has tapped the screen. we want
  212. // to read all available gestures even if a tap occurred so we clear
  213. // the queue.
  214. bool tapGesture = false;
  215. while (TouchPanel.IsGestureAvailable)
  216. {
  217. GestureSample sample = TouchPanel.ReadGesture();
  218. if (sample.GestureType == GestureType.Tap)
  219. {
  220. tapGesture = true;
  221. }
  222. }
  223. // if either the A button or the space bar was just released, or the screen
  224. // was tapped, move to the next state. Doing modulus by the number of
  225. // states lets us wrap back around to the first state.
  226. if (keyboardSpace || gamepadA || tapGesture)
  227. {
  228. currentState = (State)((int)(currentState + 1) % NumStates);
  229. }
  230. if (keyboardF)
  231. {
  232. graphics.ToggleFullScreen();
  233. //Window.Window.IsVisible = true;
  234. //Window.Window.MakeKeyAndOrderFront(Window);
  235. }
  236. lastKeyboardState = currentKeyboardState;
  237. lastGamepadState = currentGamePadState;
  238. }
  239. // a handy little function that gives a random float between two
  240. // values. This will be used in several places in the sample, in particilar in
  241. // ParticleSystem.InitializeParticle.
  242. public static float RandomBetween(float min, float max)
  243. {
  244. return min + (float)random.NextDouble() * (max - min);
  245. }
  246. }
  247. }