Game.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // Game.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. #region Using Statements
  10. using System;
  11. using System.Collections.Generic;
  12. using Microsoft.Xna.Framework;
  13. using Microsoft.Xna.Framework.Content;
  14. using Microsoft.Xna.Framework.Graphics;
  15. using Microsoft.Xna.Framework.Input;
  16. #endregion
  17. namespace Particle3DSample
  18. {
  19. /// <summary>
  20. /// Sample showing how to implement a particle system entirely
  21. /// on the GPU, using the vertex shader to animate particles.
  22. /// </summary>
  23. public class Particle3DSampleGame : Microsoft.Xna.Framework.Game
  24. {
  25. #region Fields
  26. GraphicsDeviceManager graphics;
  27. SpriteBatch spriteBatch;
  28. SpriteFont font;
  29. Model grid;
  30. // This sample uses five different particle systems.
  31. ParticleSystem explosionParticles;
  32. ParticleSystem explosionSmokeParticles;
  33. ParticleSystem projectileTrailParticles;
  34. ParticleSystem smokePlumeParticles;
  35. ParticleSystem fireParticles;
  36. // The sample can switch between three different visual effects.
  37. enum ParticleState
  38. {
  39. Explosions,
  40. SmokePlume,
  41. RingOfFire,
  42. };
  43. ParticleState currentState = ParticleState.Explosions;
  44. // The explosions effect works by firing projectiles up into the
  45. // air, so we need to keep track of all the active projectiles.
  46. List<Projectile> projectiles = new List<Projectile>();
  47. TimeSpan timeToNextProjectile = TimeSpan.Zero;
  48. // Random number generator for the fire effect.
  49. Random random = new Random();
  50. // Input state.
  51. KeyboardState currentKeyboardState;
  52. GamePadState currentGamePadState;
  53. KeyboardState lastKeyboardState;
  54. GamePadState lastGamePadState;
  55. // Camera state.
  56. float cameraArc = -5;
  57. float cameraRotation = 0;
  58. float cameraDistance = 200;
  59. #endregion
  60. #region Initialization
  61. /// <summary>
  62. /// Constructor.
  63. /// </summary>
  64. public Particle3DSampleGame()
  65. {
  66. graphics = new GraphicsDeviceManager(this);
  67. Content.RootDirectory = "Content";
  68. // Construct our particle system components.
  69. explosionParticles = new ExplosionParticleSystem(this, Content);
  70. explosionSmokeParticles = new ExplosionSmokeParticleSystem(this, Content);
  71. projectileTrailParticles = new ProjectileTrailParticleSystem(this, Content);
  72. smokePlumeParticles = new SmokePlumeParticleSystem(this, Content);
  73. fireParticles = new FireParticleSystem(this, Content);
  74. // Set the draw order so the explosions and fire
  75. // will appear over the top of the smoke.
  76. smokePlumeParticles.DrawOrder = 100;
  77. explosionSmokeParticles.DrawOrder = 200;
  78. projectileTrailParticles.DrawOrder = 300;
  79. explosionParticles.DrawOrder = 400;
  80. fireParticles.DrawOrder = 500;
  81. // Register the particle system components.
  82. Components.Add(explosionParticles);
  83. Components.Add(explosionSmokeParticles);
  84. Components.Add(projectileTrailParticles);
  85. Components.Add(smokePlumeParticles);
  86. Components.Add(fireParticles);
  87. }
  88. /// <summary>
  89. /// Load your graphics content.
  90. /// </summary>
  91. protected override void LoadContent()
  92. {
  93. spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
  94. font = Content.Load<SpriteFont>("font");
  95. grid = Content.Load<Model>("grid");
  96. }
  97. #endregion
  98. #region Update and Draw
  99. /// <summary>
  100. /// Allows the game to run logic.
  101. /// </summary>
  102. protected override void Update(GameTime gameTime)
  103. {
  104. HandleInput();
  105. UpdateCamera(gameTime);
  106. switch (currentState)
  107. {
  108. case ParticleState.Explosions:
  109. UpdateExplosions(gameTime);
  110. break;
  111. case ParticleState.SmokePlume:
  112. UpdateSmokePlume();
  113. break;
  114. case ParticleState.RingOfFire:
  115. UpdateFire();
  116. break;
  117. }
  118. UpdateProjectiles(gameTime);
  119. base.Update(gameTime);
  120. }
  121. /// <summary>
  122. /// Helper for updating the explosions effect.
  123. /// </summary>
  124. void UpdateExplosions(GameTime gameTime)
  125. {
  126. timeToNextProjectile -= gameTime.ElapsedGameTime;
  127. if (timeToNextProjectile <= TimeSpan.Zero)
  128. {
  129. // Create a new projectile once per second. The real work of moving
  130. // and creating particles is handled inside the Projectile class.
  131. projectiles.Add(new Projectile(explosionParticles,
  132. explosionSmokeParticles,
  133. projectileTrailParticles));
  134. timeToNextProjectile += TimeSpan.FromSeconds(1);
  135. }
  136. }
  137. /// <summary>
  138. /// Helper for updating the list of active projectiles.
  139. /// </summary>
  140. void UpdateProjectiles(GameTime gameTime)
  141. {
  142. int i = 0;
  143. while (i < projectiles.Count)
  144. {
  145. if (!projectiles[i].Update(gameTime))
  146. {
  147. // Remove projectiles at the end of their life.
  148. projectiles.RemoveAt(i);
  149. }
  150. else
  151. {
  152. // Advance to the next projectile.
  153. i++;
  154. }
  155. }
  156. }
  157. /// <summary>
  158. /// Helper for updating the smoke plume effect.
  159. /// </summary>
  160. void UpdateSmokePlume()
  161. {
  162. // This is trivial: we just create one new smoke particle per frame.
  163. smokePlumeParticles.AddParticle(Vector3.Zero, Vector3.Zero);
  164. }
  165. /// <summary>
  166. /// Helper for updating the fire effect.
  167. /// </summary>
  168. void UpdateFire()
  169. {
  170. const int fireParticlesPerFrame = 20;
  171. // Create a number of fire particles, randomly positioned around a circle.
  172. for (int i = 0; i < fireParticlesPerFrame; i++)
  173. {
  174. fireParticles.AddParticle(RandomPointOnCircle(), Vector3.Zero);
  175. }
  176. // Create one smoke particle per frmae, too.
  177. smokePlumeParticles.AddParticle(RandomPointOnCircle(), Vector3.Zero);
  178. }
  179. /// <summary>
  180. /// Helper used by the UpdateFire method. Chooses a random location
  181. /// around a circle, at which a fire particle will be created.
  182. /// </summary>
  183. Vector3 RandomPointOnCircle()
  184. {
  185. const float radius = 30;
  186. const float height = 40;
  187. double angle = random.NextDouble() * Math.PI * 2;
  188. float x = (float)Math.Cos(angle);
  189. float y = (float)Math.Sin(angle);
  190. return new Vector3(x * radius, y * radius + height, 0);
  191. }
  192. /// <summary>
  193. /// This is called when the game should draw itself.
  194. /// </summary>
  195. protected override void Draw(GameTime gameTime)
  196. {
  197. GraphicsDevice device = graphics.GraphicsDevice;
  198. device.Clear(Color.CornflowerBlue);
  199. // Compute camera matrices.
  200. float aspectRatio = (float)device.Viewport.Width /
  201. (float)device.Viewport.Height;
  202. Matrix view = Matrix.CreateTranslation(0, -25, 0) *
  203. Matrix.CreateRotationY(MathHelper.ToRadians(cameraRotation)) *
  204. Matrix.CreateRotationX(MathHelper.ToRadians(cameraArc)) *
  205. Matrix.CreateLookAt(new Vector3(0, 0, -cameraDistance),
  206. new Vector3(0, 0, 0), Vector3.Up);
  207. Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
  208. aspectRatio,
  209. 1, 10000);
  210. // Pass camera matrices through to the particle system components.
  211. explosionParticles.SetCamera(view, projection);
  212. explosionSmokeParticles.SetCamera(view, projection);
  213. projectileTrailParticles.SetCamera(view, projection);
  214. smokePlumeParticles.SetCamera(view, projection);
  215. fireParticles.SetCamera(view, projection);
  216. // Draw our background grid and message text.
  217. DrawGrid(view, projection);
  218. DrawMessage();
  219. // This will draw the particle system components.
  220. base.Draw(gameTime);
  221. }
  222. /// <summary>
  223. /// Helper for drawing the background grid model.
  224. /// </summary>
  225. void DrawGrid(Matrix view, Matrix projection)
  226. {
  227. GraphicsDevice device = graphics.GraphicsDevice;
  228. device.BlendState = BlendState.Opaque;
  229. device.DepthStencilState = DepthStencilState.Default;
  230. device.SamplerStates[0] = SamplerState.LinearWrap;
  231. grid.Draw(Matrix.Identity, view, projection);
  232. }
  233. /// <summary>
  234. /// Helper for drawing our message text.
  235. /// </summary>
  236. void DrawMessage()
  237. {
  238. string message = string.Format("Current effect: {0}!!!\n" +
  239. "Hit the A button or space bar to switch.",
  240. currentState);
  241. spriteBatch.Begin();
  242. spriteBatch.DrawString(font, message, new Vector2(50, 50), Color.White);
  243. spriteBatch.End();
  244. }
  245. #endregion
  246. #region Handle Input
  247. /// <summary>
  248. /// Handles input for quitting the game and cycling
  249. /// through the different particle effects.
  250. /// </summary>
  251. void HandleInput()
  252. {
  253. lastKeyboardState = currentKeyboardState;
  254. lastGamePadState = currentGamePadState;
  255. currentKeyboardState = Keyboard.GetState();
  256. currentGamePadState = GamePad.GetState(PlayerIndex.One);
  257. // Check for exit.
  258. if (currentKeyboardState.IsKeyDown(Keys.Escape) ||
  259. currentGamePadState.Buttons.Back == ButtonState.Pressed)
  260. {
  261. Exit();
  262. }
  263. // Check for changing the active particle effect.
  264. if (((currentKeyboardState.IsKeyDown(Keys.Space) &&
  265. (lastKeyboardState.IsKeyUp(Keys.Space))) ||
  266. ((currentGamePadState.Buttons.A == ButtonState.Pressed)) &&
  267. (lastGamePadState.Buttons.A == ButtonState.Released)))
  268. {
  269. currentState++;
  270. if (currentState > ParticleState.RingOfFire)
  271. currentState = 0;
  272. }
  273. }
  274. /// <summary>
  275. /// Handles input for moving the camera.
  276. /// </summary>
  277. void UpdateCamera(GameTime gameTime)
  278. {
  279. float time = (float)gameTime.ElapsedGameTime.TotalMilliseconds;
  280. // Check for input to rotate the camera up and down around the model.
  281. if (currentKeyboardState.IsKeyDown(Keys.Up) ||
  282. currentKeyboardState.IsKeyDown(Keys.W))
  283. {
  284. cameraArc += time * 0.025f;
  285. }
  286. if (currentKeyboardState.IsKeyDown(Keys.Down) ||
  287. currentKeyboardState.IsKeyDown(Keys.S))
  288. {
  289. cameraArc -= time * 0.025f;
  290. }
  291. cameraArc += currentGamePadState.ThumbSticks.Right.Y * time * 0.05f;
  292. // Limit the arc movement.
  293. if (cameraArc > 90.0f)
  294. cameraArc = 90.0f;
  295. else if (cameraArc < -90.0f)
  296. cameraArc = -90.0f;
  297. // Check for input to rotate the camera around the model.
  298. if (currentKeyboardState.IsKeyDown(Keys.Right) ||
  299. currentKeyboardState.IsKeyDown(Keys.D))
  300. {
  301. cameraRotation += time * 0.05f;
  302. }
  303. if (currentKeyboardState.IsKeyDown(Keys.Left) ||
  304. currentKeyboardState.IsKeyDown(Keys.A))
  305. {
  306. cameraRotation -= time * 0.05f;
  307. }
  308. cameraRotation += currentGamePadState.ThumbSticks.Right.X * time * 0.1f;
  309. // Check for input to zoom camera in and out.
  310. if (currentKeyboardState.IsKeyDown(Keys.Z))
  311. cameraDistance += time * 0.25f;
  312. if (currentKeyboardState.IsKeyDown(Keys.X))
  313. cameraDistance -= time * 0.25f;
  314. cameraDistance += currentGamePadState.Triggers.Left * time * 0.5f;
  315. cameraDistance -= currentGamePadState.Triggers.Right * time * 0.5f;
  316. // Limit the camera distance.
  317. if (cameraDistance > 500)
  318. cameraDistance = 500;
  319. else if (cameraDistance < 10)
  320. cameraDistance = 10;
  321. if (currentGamePadState.Buttons.RightStick == ButtonState.Pressed ||
  322. currentKeyboardState.IsKeyDown(Keys.R))
  323. {
  324. cameraArc = -5;
  325. cameraRotation = 0;
  326. cameraDistance = 200;
  327. }
  328. }
  329. #endregion
  330. }
  331. #region Entry Point
  332. /// <summary>
  333. /// The main entry point for the application.
  334. /// </summary>
  335. static class Program
  336. {
  337. static void Main()
  338. {
  339. using (Particle3DSampleGame game = new Particle3DSampleGame())
  340. {
  341. game.Run();
  342. }
  343. }
  344. }
  345. #endregion
  346. }