2
0

PerformanceMeasuringGame.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // PerformanceMeasuringGame.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. using System;
  10. using Microsoft.Xna.Framework;
  11. using Microsoft.Xna.Framework.Graphics;
  12. using Microsoft.Xna.Framework.Input;
  13. using Microsoft.Xna.Framework.Input.Touch;
  14. using PerformanceMeasuring.GameDebugTools;
  15. namespace PerformanceMeasuring
  16. {
  17. /// <summary>
  18. /// This sample game shows how to use the GameDebugTools to measure the performance of a game,
  19. /// as well as how the number of objects and interactions between them can affect performance.
  20. /// </summary>
  21. public class PerformanceMeasuringGame : Game
  22. {
  23. // The maximum number of spheres in our world
  24. const int maximumNumberOfSpheres = 200;
  25. GraphicsDeviceManager graphics;
  26. // A SpriteBatch, font, and blank texture for drawing our instruction text
  27. SpriteBatch spriteBatch;
  28. SpriteFont font;
  29. Texture2D blank;
  30. // The text we draw as instructions.
  31. string instructions =
  32. #if WINDOWS_PHONE
  33. "Tap - Toggle collisions\nDrag up/down - Change number of spheres";
  34. #else
  35. "X - Toggle collisions\nUp - Increase number of spheres\nDown - Decrease number of spheres";
  36. #endif
  37. // The size of the world. The world is a bounding box ranging from -worldSize to worldSize on
  38. // the X and Z axis, and from 0 to worldSize on the Y axis.
  39. const float worldSize = 20f;
  40. // A model for our ground
  41. Model ground;
  42. // An array of spheres and the number of currently active spheres
  43. Sphere[] spheres = new Sphere[maximumNumberOfSpheres];
  44. int activeSphereCount = 50;
  45. // Are we colliding the spheres against each other?
  46. bool collideSpheres = true;
  47. // Various input states for changing the simulation
  48. GamePadState gamePad, gamePadPrev;
  49. KeyboardState keyboard, keyboardPrev;
  50. public PerformanceMeasuringGame()
  51. {
  52. graphics = new GraphicsDeviceManager(this);
  53. Content.RootDirectory = "Content";
  54. #if WINDOWS_PHONE
  55. TargetElapsedTime = TimeSpan.FromTicks(333333);
  56. graphics.IsFullScreen = true;
  57. #endif
  58. }
  59. /// <summary>
  60. /// Allows the game to perform any initialization it needs to before starting to run.
  61. /// This is where it can query for any required services and load any non-graphic
  62. /// related content. Calling base.Initialize will enumerate through any components
  63. /// and initialize them as well.
  64. /// </summary>
  65. protected override void Initialize()
  66. {
  67. // Initialize the DebugSystem and change the visibility of the FpsCounter and TimeRuler.
  68. // The FpsCounter will show us our current "frames per second". On Windows Phone we can
  69. // have a maximum of 30 frames per second. On Windows and Xbox, we can reach 60 frames
  70. // per second with our current game settings.
  71. // The TimeRuler allows us to instrument our code and figure out where our bottlenecks
  72. // are so we can speed up slow code.
  73. DebugSystem.Initialize(this, "Font");
  74. DebugSystem.Instance.FpsCounter.Visible = true;
  75. DebugSystem.Instance.TimeRuler.Visible = true;
  76. DebugSystem.Instance.TimeRuler.ShowLog = true;
  77. // Enable the Tap and FreeDrag gestures for our input on Windows Phone
  78. TouchPanel.EnabledGestures = GestureType.Tap | GestureType.FreeDrag;
  79. base.Initialize();
  80. }
  81. /// <summary>
  82. /// LoadContent will be called once per game and is the place to load
  83. /// all of your content.
  84. /// </summary>
  85. protected override void LoadContent()
  86. {
  87. // Create a new SpriteBatch, which can be used to draw textures.
  88. spriteBatch = new SpriteBatch(GraphicsDevice);
  89. // Load the font for our UI
  90. font = Content.Load<SpriteFont>("Font");
  91. // Create a blank texture for our UI drawing
  92. blank = new Texture2D(GraphicsDevice, 1, 1);
  93. blank.SetData(new[] { Color.White });
  94. // Load the ground model
  95. ground = Content.Load<Model>("Ground");
  96. // Create our spheres
  97. CreateSpheres();
  98. }
  99. /// <summary>
  100. /// Helper method that creates all of our spheres.
  101. /// </summary>
  102. private void CreateSpheres()
  103. {
  104. // Create a random number generator
  105. Random random = new Random();
  106. // These are the various colors we use when creating the spheres
  107. Color[] sphereColors = new[]
  108. {
  109. Color.Red,
  110. Color.Blue,
  111. Color.Green,
  112. Color.Orange,
  113. Color.Pink,
  114. Color.Purple,
  115. Color.Yellow
  116. };
  117. // The radius of a sphere
  118. const float radius = 1f;
  119. for (int i = 0; i < maximumNumberOfSpheres; i++)
  120. {
  121. // Create the sphere
  122. Sphere sphere = new Sphere(GraphicsDevice, radius);
  123. // Position the sphere in our world
  124. sphere.Position = new Vector3(
  125. RandomFloat(random, -worldSize + radius, worldSize - radius),
  126. RandomFloat(random, radius, worldSize - radius),
  127. RandomFloat(random, -worldSize + radius, worldSize - radius));
  128. // Pick a random color for the sphere
  129. sphere.Color = sphereColors[random.Next(sphereColors.Length)];
  130. // Create a random velocity vector
  131. sphere.Velocity = new Vector3(
  132. RandomFloat(random, -10f, 10f),
  133. RandomFloat(random, -10f, 10f),
  134. RandomFloat(random, -10f, 10f));
  135. // Add the sphere to our array
  136. spheres[i] = sphere;
  137. }
  138. }
  139. /// <summary>
  140. /// A helper method that generates a random float in a given range.
  141. /// </summary>
  142. private static float RandomFloat(Random random, float min, float max)
  143. {
  144. return (float)random.NextDouble() * (max - min) + min;
  145. }
  146. /// <summary>
  147. /// Allows the game to run logic such as updating the world,
  148. /// checking for collisions, gathering input, and playing audio.
  149. /// </summary>
  150. /// <param name="gameTime">Provides a snapshot of timing values.</param>
  151. protected override void Update(GameTime gameTime)
  152. {
  153. // We must call StartFrame at the top of Update to indicate to the TimeRuler
  154. // that a new frame has started.
  155. DebugSystem.Instance.TimeRuler.StartFrame();
  156. // We can now begin measuring our Update method
  157. DebugSystem.Instance.TimeRuler.BeginMark("Update", Color.Blue);
  158. // Update the input state for the game
  159. UpdateInput(gameTime);
  160. // Update all of the spheres in the game, handling collision if desired
  161. UpdateSpheres(gameTime);
  162. base.Update(gameTime);
  163. // End measuring the Update method
  164. DebugSystem.Instance.TimeRuler.EndMark("Update");
  165. }
  166. /// <summary>
  167. /// Helper method to handle the input for the sample.
  168. /// </summary>
  169. private void UpdateInput(GameTime gameTime)
  170. {
  171. // Update the game pad and keyboard input states
  172. gamePadPrev = gamePad;
  173. gamePad = GamePad.GetState(PlayerIndex.One);
  174. keyboardPrev = keyboard;
  175. keyboard = Keyboard.GetState();
  176. // If we've pressed the Back button or Escape key, exit the game
  177. if (gamePad.IsButtonDown(Buttons.Back) || keyboard.IsKeyDown(Keys.Escape))
  178. Exit();
  179. // If the X key or button was pressed, toggle whether or not we're colliding the spheres
  180. if ((gamePad.IsButtonDown(Buttons.X) && gamePadPrev.IsButtonUp(Buttons.X)) ||
  181. (keyboard.IsKeyDown(Keys.X) && keyboardPrev.IsKeyUp(Keys.X)))
  182. {
  183. collideSpheres = !collideSpheres;
  184. }
  185. // If the user pressed Up or Down on the keyboard, DPad, or left thumbstick, we adjust
  186. // the number of active spheres in our scene
  187. if (gamePad.IsButtonDown(Buttons.DPadUp) ||
  188. gamePad.IsButtonDown(Buttons.LeftThumbstickUp) ||
  189. keyboard.IsKeyDown(Keys.Up))
  190. {
  191. activeSphereCount++;
  192. }
  193. else if (gamePad.IsButtonDown(Buttons.DPadDown) ||
  194. gamePad.IsButtonDown(Buttons.LeftThumbstickDown) ||
  195. keyboard.IsKeyDown(Keys.Down))
  196. {
  197. activeSphereCount--;
  198. }
  199. // Poll for all the gestures our game received
  200. while (TouchPanel.IsGestureAvailable)
  201. {
  202. GestureSample gesture = TouchPanel.ReadGesture();
  203. // If the user tapped the screen, toggle whether or not we're colliding the spheres
  204. if (gesture.GestureType == GestureType.Tap)
  205. {
  206. collideSpheres = !collideSpheres;
  207. }
  208. // If the user dragged on the screen, we adjust the number of active spheres in the scene
  209. else if (gesture.GestureType == GestureType.FreeDrag)
  210. {
  211. activeSphereCount -= Math.Sign(gesture.Delta.Y);
  212. }
  213. }
  214. // Make sure we clamp our active sphere count so that we don't go above our maximum or below 1
  215. activeSphereCount = Math.Max(Math.Min(activeSphereCount, maximumNumberOfSpheres), 1);
  216. }
  217. /// <summary>
  218. /// Helper method that updates our sphere simulation.
  219. /// </summary>
  220. private void UpdateSpheres(GameTime gameTime)
  221. {
  222. // Update all spheres and perform collision against the world
  223. for (int i = 0; i < activeSphereCount; i++)
  224. {
  225. Sphere s = spheres[i];
  226. s.Update(gameTime);
  227. BounceSphereInWorld(s);
  228. }
  229. // If we are colliding spheres against each other
  230. if (collideSpheres)
  231. {
  232. // Iterate over the list twice to compare the spheres against each other
  233. for (int i = 0; i < activeSphereCount; i++)
  234. {
  235. for (int j = 0; j < activeSphereCount; j++)
  236. {
  237. // Make sure we don't collid a sphere with itself
  238. if (i == j)
  239. continue;
  240. // Get the spheres
  241. Sphere a = spheres[i];
  242. Sphere b = spheres[j];
  243. // If the spheres are intersecting
  244. if (a.Bounds.Intersects(b.Bounds))
  245. {
  246. // Get the vector between their centers
  247. Vector3 delta = b.Position - a.Position;
  248. // Calculate the point halfway between the spheres
  249. Vector3 center = a.Position + delta / 2f;
  250. // Normalize the delta vector
  251. delta.Normalize();
  252. // Move the spheres to resolve the collision
  253. a.Position = center - delta * a.Radius;
  254. b.Position = center + delta * b.Radius;
  255. // Reflect the velocities to bounce the spheres
  256. a.Velocity = Vector3.Normalize(Vector3.Reflect(a.Velocity, delta)) * b.Velocity.Length();
  257. b.Velocity = Vector3.Normalize(Vector3.Reflect(b.Velocity, delta)) * a.Velocity.Length();
  258. }
  259. }
  260. }
  261. }
  262. }
  263. /// <summary>
  264. /// Helper method that keeps a sphere in the world by bouncing it off the walls.
  265. /// </summary>
  266. private static void BounceSphereInWorld(Sphere s)
  267. {
  268. // First test along the X axis, flipping the velocity if a collision occurs.
  269. if (s.Position.X < -worldSize + s.Radius)
  270. {
  271. s.Position.X = -worldSize + s.Radius;
  272. if (s.Velocity.X < 0f)
  273. s.Velocity.X *= -1f;
  274. }
  275. else if (s.Position.X > worldSize - s.Radius)
  276. {
  277. s.Position.X = worldSize - s.Radius;
  278. if (s.Velocity.X > 0f)
  279. s.Velocity.X *= -1f;
  280. }
  281. // Then we test the Y axis
  282. if (s.Position.Y < s.Radius)
  283. {
  284. s.Position.Y = s.Radius;
  285. if (s.Velocity.Y < 0f)
  286. s.Velocity.Y *= -1f;
  287. }
  288. else if (s.Position.Y > worldSize - s.Radius)
  289. {
  290. s.Position.Y = worldSize - s.Radius;
  291. if (s.Velocity.Y > 0f)
  292. s.Velocity.Y *= -1f;
  293. }
  294. // And lastly the Z axis
  295. if (s.Position.Z < -worldSize + s.Radius)
  296. {
  297. s.Position.Z = -worldSize + s.Radius;
  298. if (s.Velocity.Z < 0f)
  299. s.Velocity.Z *= -1f;
  300. }
  301. else if (s.Position.Z > worldSize - s.Radius)
  302. {
  303. s.Position.Z = worldSize - s.Radius;
  304. if (s.Velocity.Z > 0f)
  305. s.Velocity.Z *= -1f;
  306. }
  307. }
  308. /// <summary>
  309. /// This is called when the game should draw itself.
  310. /// </summary>
  311. /// <param name="gameTime">Provides a snapshot of timing values.</param>
  312. protected override void Draw(GameTime gameTime)
  313. {
  314. // Begin measuring our Draw method
  315. DebugSystem.Instance.TimeRuler.BeginMark("Draw", Color.Red);
  316. GraphicsDevice.Clear(Color.CornflowerBlue);
  317. // Create a view and projection matrix for our camera
  318. Matrix view = Matrix.CreateLookAt(
  319. new Vector3(worldSize, worldSize, worldSize) * 1.5f, Vector3.Zero, Vector3.Up);
  320. Matrix projection = Matrix.CreatePerspectiveFieldOfView(
  321. MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 0.01f, 100f);
  322. // Set our sampler state to allow the ground to have a repeated texture
  323. GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
  324. // Draw the ground scaled to our world
  325. ground.Draw(Matrix.CreateScale(worldSize, 1f, worldSize), view, projection);
  326. // Draw all of our spheres
  327. for (int i = 0; i < activeSphereCount; i++)
  328. {
  329. spheres[i].Draw(view, projection);
  330. }
  331. // Draw the demo text on top of the scene
  332. DrawDemoText();
  333. base.Draw(gameTime);
  334. // End measuring our Draw method
  335. DebugSystem.Instance.TimeRuler.EndMark("Draw");
  336. }
  337. /// <summary>
  338. /// Helper that draws our demo text, including our current settings and the instructions.
  339. /// </summary>
  340. private void DrawDemoText()
  341. {
  342. // Create the text based on our current states
  343. string demoText = string.Format(
  344. "Sphere count: {0}\nCollisions Enabled: {1}\n\n{2}",
  345. activeSphereCount, collideSpheres, instructions);
  346. // Measure our text and calculate the correct position to draw it
  347. Vector2 size = font.MeasureString(demoText);
  348. Vector2 pos = new Vector2(
  349. GraphicsDevice.Viewport.TitleSafeArea.Right - size.X,
  350. GraphicsDevice.Viewport.TitleSafeArea.Top);
  351. spriteBatch.Begin();
  352. // Draw a blank box as a background for our text
  353. spriteBatch.Draw(
  354. blank,
  355. new Rectangle((int)pos.X - 5, (int)pos.Y, (int)size.X + 10, (int)size.Y + 5),
  356. Color.Black * .5f);
  357. // Draw the text itself
  358. spriteBatch.DrawString(font, demoText, pos, Color.White);
  359. spriteBatch.End();
  360. }
  361. }
  362. }