PerformanceMeasuringGame.cs 16 KB

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