Game.cs 14 KB

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