Game1.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  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.Audio;
  14. using Microsoft.Xna.Framework.Content;
  15. using Microsoft.Xna.Framework.Graphics;
  16. using Microsoft.Xna.Framework.Input;
  17. using Microsoft.Xna.Framework.Storage;
  18. #endregion
  19. namespace TestDataSetAndGet
  20. {
  21. /// <summary>
  22. /// This is the main type for your game
  23. /// </summary>
  24. public class PerPixelCollisionGame : Microsoft.Xna.Framework.Game
  25. {
  26. GraphicsDeviceManager graphics;
  27. // The images we will draw
  28. Texture2D personTexture;
  29. Texture2D blockTexture;
  30. Texture2D characterTexture;
  31. // The color data for the images; used for per pixel collision
  32. Color[] personTextureData;
  33. Color[] blockTextureData;
  34. Color[] characterTextureData;
  35. // The images will be drawn with this SpriteBatch
  36. SpriteBatch spriteBatch;
  37. // Person
  38. Vector2 personPosition;
  39. const int PersonMoveSpeed = 5;
  40. // Blocks
  41. List<Vector2> blockPositions = new List<Vector2>();
  42. float BlockSpawnProbability = 0.01f;
  43. const int BlockFallSpeed = 2;
  44. Random random = new Random();
  45. // For when a collision is detected
  46. bool personHit = false;
  47. // The sub-rectangle of the drawable area which should be visible on all TVs
  48. Rectangle safeBounds;
  49. // Percentage of the screen on every side is the safe area
  50. const float SafeAreaPortion = 0.05f;
  51. public PerPixelCollisionGame()
  52. {
  53. graphics = new GraphicsDeviceManager(this);
  54. graphics.SynchronizeWithVerticalRetrace = false;
  55. Content.RootDirectory = "Content";
  56. }
  57. /// <summary>
  58. /// Allows the game to perform any initialization it needs to before starting to
  59. /// run. This is where it can query for any required services and load any
  60. /// non-graphic related content. Calling base.Initialize will enumerate through
  61. /// any components and initialize them as well.
  62. /// </summary>
  63. protected override void Initialize()
  64. {
  65. base.Initialize();
  66. // Calculate safe bounds based on current resolution
  67. Viewport viewport = graphics.GraphicsDevice.Viewport;
  68. safeBounds = new Rectangle(
  69. (int)(viewport.Width * SafeAreaPortion),
  70. (int)(viewport.Height * SafeAreaPortion),
  71. (int)(viewport.Width * (1 - 2 * SafeAreaPortion)),
  72. (int)(viewport.Height * (1 - 2 * SafeAreaPortion)));
  73. // Start the player in the center along the bottom of the screen
  74. personPosition.X = (safeBounds.Width - personTexture.Width) / 2;
  75. personPosition.Y = safeBounds.Height - personTexture.Height;
  76. }
  77. /// <summary>
  78. /// Load your graphics content.
  79. /// </summary>
  80. protected override void LoadContent()
  81. {
  82. // Load textures
  83. blockTexture = Content.Load<Texture2D>("Block");
  84. personTexture = Content.Load<Texture2D>("Character");
  85. //personTexture = Content.Load<Texture2D>("Warrior3Active");
  86. characterTexture = new RenderTarget2D(GraphicsDevice, personTexture.Width, personTexture.Height);
  87. // Extract collision data
  88. blockTextureData =
  89. new Color[blockTexture.Width * blockTexture.Height];
  90. blockTexture.GetData(blockTextureData);
  91. personTextureData =
  92. new Color[personTexture.Width * personTexture.Height];
  93. personTexture.GetData(personTextureData);
  94. characterTexture.SetData(personTextureData);
  95. characterTextureData =
  96. new Color[characterTexture.Width * characterTexture.Height];
  97. characterTexture.GetData(characterTextureData);
  98. //personTexture.SetData(characterTextureData);
  99. // Create a sprite batch to draw those textures
  100. spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
  101. }
  102. /// <summary>
  103. /// Allows the game to run logic such as updating the world,
  104. /// checking for collisions, gathering input and playing audio.
  105. /// </summary>
  106. /// <param name="gameTime">Provides a snapshot of timing values.</param>
  107. protected override void Update(GameTime gameTime)
  108. {
  109. // Get input
  110. KeyboardState keyboard = Keyboard.GetState();
  111. GamePadState gamePad = GamePad.GetState(PlayerIndex.One);
  112. // Allows the game to exit
  113. if (gamePad.Buttons.Back == ButtonState.Pressed ||
  114. keyboard.IsKeyDown(Keys.Escape))
  115. {
  116. this.Exit();
  117. }
  118. // Move the player left and right with arrow keys or d-pad
  119. if (keyboard.IsKeyDown(Keys.Left) ||
  120. gamePad.DPad.Left == ButtonState.Pressed)
  121. {
  122. personPosition.X -= PersonMoveSpeed;
  123. }
  124. if (keyboard.IsKeyDown(Keys.Right) ||
  125. gamePad.DPad.Right == ButtonState.Pressed)
  126. {
  127. personPosition.X += PersonMoveSpeed;
  128. }
  129. // Prevent the person from moving off of the screen
  130. personPosition.X = MathHelper.Clamp(personPosition.X,
  131. safeBounds.Left, safeBounds.Right - personTexture.Width);
  132. // Spawn new falling blocks
  133. if (random.NextDouble() < BlockSpawnProbability)
  134. {
  135. float x = (float)random.NextDouble() *
  136. (Window.ClientBounds.Width - blockTexture.Width);
  137. blockPositions.Add(new Vector2(x, -blockTexture.Height));
  138. }
  139. // Get the bounding rectangle of the person
  140. Rectangle personRectangle =
  141. new Rectangle((int)personPosition.X, (int)personPosition.Y,
  142. personTexture.Width, personTexture.Height);
  143. // Update each block
  144. personHit = false;
  145. for (int i = 0; i < blockPositions.Count; i++)
  146. {
  147. // Animate this block falling
  148. blockPositions[i] =
  149. new Vector2(blockPositions[i].X,
  150. blockPositions[i].Y + BlockFallSpeed);
  151. // Get the bounding rectangle of this block
  152. Rectangle blockRectangle =
  153. new Rectangle((int)blockPositions[i].X, (int)blockPositions[i].Y,
  154. blockTexture.Width, blockTexture.Height);
  155. // Check collision with person
  156. if (IntersectPixels(personRectangle, personTextureData,
  157. blockRectangle, blockTextureData))
  158. {
  159. personHit = true;
  160. }
  161. // Remove this block if it have fallen off the screen
  162. if (blockPositions[i].Y > Window.ClientBounds.Height)
  163. {
  164. blockPositions.RemoveAt(i);
  165. // When removing a block, the next block will have the same index
  166. // as the current block. Decrement i to prevent skipping a block.
  167. i--;
  168. }
  169. }
  170. base.Update(gameTime);
  171. }
  172. /// <summary>
  173. /// This is called when the game should draw itself.
  174. /// </summary>
  175. /// <param name="gameTime">Provides a snapshot of timing values.</param>
  176. protected override void Draw(GameTime gameTime)
  177. {
  178. GraphicsDevice device = graphics.GraphicsDevice;
  179. // Change the background to red when the person was hit by a block
  180. if (personHit)
  181. {
  182. device.Clear(Color.Red);
  183. }
  184. else
  185. {
  186. device.Clear(Color.CornflowerBlue);
  187. }
  188. spriteBatch.Begin();
  189. // Draw person
  190. spriteBatch.Draw(personTexture, personPosition, Color.White);
  191. Vector2 characterPosition = personPosition;
  192. characterPosition.X += personTexture.Width;
  193. spriteBatch.Draw(characterTexture, characterPosition, Color.White);
  194. // Draw blocks
  195. foreach (Vector2 blockPosition in blockPositions)
  196. spriteBatch.Draw(blockTexture, blockPosition, Color.White);
  197. spriteBatch.End();
  198. base.Draw(gameTime);
  199. }
  200. /// <summary>
  201. /// Determines if there is overlap of the non-transparent pixels
  202. /// between two sprites.
  203. /// </summary>
  204. /// <param name="rectangleA">Bounding rectangle of the first sprite</param>
  205. /// <param name="dataA">Pixel data of the first sprite</param>
  206. /// <param name="rectangleB">Bouding rectangle of the second sprite</param>
  207. /// <param name="dataB">Pixel data of the second sprite</param>
  208. /// <returns>True if non-transparent pixels overlap; false otherwise</returns>
  209. static bool IntersectPixels(Rectangle rectangleA, Color[] dataA,
  210. Rectangle rectangleB, Color[] dataB)
  211. {
  212. // Find the bounds of the rectangle intersection
  213. int top = Math.Max(rectangleA.Top, rectangleB.Top);
  214. int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
  215. int left = Math.Max(rectangleA.Left, rectangleB.Left);
  216. int right = Math.Min(rectangleA.Right, rectangleB.Right);
  217. // Check every point within the intersection bounds
  218. for (int y = top; y < bottom; y++)
  219. {
  220. for (int x = left; x < right; x++)
  221. {
  222. // Get the color of both pixels at this point
  223. Color colorA = dataA[(x - rectangleA.Left) +
  224. (y - rectangleA.Top) * rectangleA.Width];
  225. Color colorB = dataB[(x - rectangleB.Left) +
  226. (y - rectangleB.Top) * rectangleB.Width];
  227. // If both pixels are not completely transparent,
  228. if (colorA.A != 0 && colorB.A != 0)
  229. {
  230. // then an intersection has been found
  231. return true;
  232. }
  233. }
  234. }
  235. // No intersection found
  236. return false;
  237. }
  238. }
  239. }