Advertisement
Guest User

Untitled

a guest
Aug 20th, 2015
257
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 17.06 KB | None | 0 0
  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.  
  10. #region Using Statements
  11. using System;
  12. using System.Collections.Generic;
  13. using Microsoft.Xna.Framework;
  14. using Microsoft.Xna.Framework.Audio;
  15. using Microsoft.Xna.Framework.Content;
  16. using Microsoft.Xna.Framework.Graphics;
  17. using Microsoft.Xna.Framework.Input;
  18. using Microsoft.Xna.Framework.Storage;
  19. #endregion
  20.  
  21. namespace TransformedCollision
  22. {
  23.     /// <summary>
  24.     /// This is the main type for your game
  25.     /// </summary>
  26.     public class TransformedCollisionGame : Microsoft.Xna.Framework.Game
  27.     {
  28.         GraphicsDeviceManager graphics;
  29.  
  30.         // The images we will draw
  31.         Texture2D personTexture;
  32.         Texture2D blockTexture;
  33.  
  34.         // The color data for the images; used for per pixel collision
  35.         Color[] personTextureData;
  36.         Color[] blockTextureData;
  37.  
  38.         // The images will be drawn with this SpriteBatch
  39.         SpriteBatch spriteBatch;
  40.  
  41.         // Person
  42.         Vector2 personPosition;
  43.         const int PersonMoveSpeed = 5;
  44.  
  45.         // Blocks
  46.         List<Block> blocks = new List<Block>();
  47.         float BlockSpawnProbability = 0.01f;
  48.         const int BlockFallSpeed = 1;
  49.         const float BlockRotateSpeed = 0.005f;
  50.         Vector2 blockOrigin;
  51.  
  52.         Random random = new Random();
  53.  
  54.         // For when a collision is detected
  55.         bool personHit = false;
  56.  
  57.         // The sub-rectangle of the drawable area which should be visible on all TVs
  58.         Rectangle safeBounds;
  59.         // Percentage of the screen on every side is the safe area
  60.         const float SafeAreaPortion = 0.05f;
  61.  
  62.  
  63.         public TransformedCollisionGame()
  64.         {
  65.             graphics = new GraphicsDeviceManager(this);
  66.             Content.RootDirectory = "Content";
  67.         }
  68.  
  69.  
  70.         /// <summary>
  71.         /// Allows the game to perform any initialization it needs to before starting to
  72.         /// run. This is where it can query for any required services and load any
  73.         /// non-graphic related content.  Calling base.Initialize will enumerate through
  74.         /// any components and initialize them as well.
  75.         /// </summary>
  76.         protected override void Initialize()
  77.         {
  78.             base.Initialize();
  79.  
  80.             // Calculate safe bounds based on current resolution
  81.             Viewport viewport = graphics.GraphicsDevice.Viewport;
  82.             safeBounds = new Rectangle(
  83.                 (int)(viewport.Width * SafeAreaPortion),
  84.                 (int)(viewport.Height * SafeAreaPortion),
  85.                 (int)(viewport.Width * (1 - 2 * SafeAreaPortion)),
  86.                 (int)(viewport.Height * (1 - 2 * SafeAreaPortion)));
  87.             // Start the player in the center along the bottom of the screen
  88.             personPosition.X = (safeBounds.Width - personTexture.Width) / 2;
  89.             personPosition.Y = safeBounds.Height - personTexture.Height;
  90.         }
  91.  
  92.  
  93.         /// <summary>
  94.         /// Load your graphics content.
  95.         /// </summary>
  96.         protected override void LoadContent()
  97.         {
  98.             // Load textures
  99.             blockTexture = Content.Load<Texture2D>("SpinnerBlock");
  100.             personTexture = Content.Load<Texture2D>("Person");
  101.  
  102.             // Extract collision data
  103.             blockTextureData =
  104.                 new Color[blockTexture.Width * blockTexture.Height];
  105.             blockTexture.GetData(blockTextureData);
  106.             personTextureData =
  107.                 new Color[personTexture.Width * personTexture.Height];
  108.             personTexture.GetData(personTextureData);
  109.  
  110.             // Calculate the block origin
  111.             blockOrigin =
  112.                 new Vector2(blockTexture.Width / 2, blockTexture.Height / 2);
  113.  
  114.             // Create a sprite batch to draw those textures
  115.             spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
  116.         }
  117.  
  118.  
  119.         /// <summary>
  120.         /// Allows the game to run logic such as updating the world,
  121.         /// checking for collisions, gathering input and playing audio.
  122.         /// </summary>
  123.         /// <param name="gameTime">Provides a snapshot of timing values.</param>
  124.         protected override void Update(GameTime gameTime)
  125.         {
  126.             // Get input
  127.             KeyboardState keyboard = Keyboard.GetState();
  128.             GamePadState gamePad = GamePad.GetState(PlayerIndex.One);
  129.  
  130.             // Allows the game to exit
  131.             if (gamePad.Buttons.Back == ButtonState.Pressed ||
  132.                 keyboard.IsKeyDown(Keys.Escape))
  133.             {
  134.                 this.Exit();
  135.             }
  136.  
  137.             // Move the player left and right with arrow keys or d-pad
  138.             if (keyboard.IsKeyDown(Keys.Left) ||
  139.                 gamePad.DPad.Left == ButtonState.Pressed)
  140.             {
  141.                 personPosition.X -= PersonMoveSpeed;
  142.             }
  143.             if (keyboard.IsKeyDown(Keys.Right) ||
  144.                 gamePad.DPad.Right == ButtonState.Pressed)
  145.             {
  146.                 personPosition.X += PersonMoveSpeed;
  147.             }
  148.  
  149.             // Prevent the person from moving off of the screen
  150.             personPosition.X = MathHelper.Clamp(personPosition.X,
  151.                 safeBounds.Left, safeBounds.Right - personTexture.Width);
  152.  
  153.             // Update the person's transform
  154.             Matrix personTransform =
  155.                 Matrix.CreateTranslation(new Vector3(personPosition, 0.0f));
  156.  
  157.             // Spawn new falling blocks
  158.             if (random.NextDouble() < BlockSpawnProbability)
  159.             {
  160.                 Block newBlock = new Block();
  161.  
  162.                 // at a random position just above the screen
  163.                 float x = (float)random.NextDouble() *
  164.                     (Window.ClientBounds.Width - blockTexture.Width);
  165.                 newBlock.Position = new Vector2(x, -blockTexture.Height);
  166.  
  167.                 // with a random rotation
  168.                 newBlock.Rotation = (float)random.NextDouble() * MathHelper.TwoPi;
  169.  
  170.                 blocks.Add(newBlock);
  171.             }
  172.  
  173.             // Get the bounding rectangle of the person
  174.             Rectangle personRectangle =
  175.                 new Rectangle((int)personPosition.X, (int)personPosition.Y,
  176.                 personTexture.Width, personTexture.Height);
  177.  
  178.             // Update each block
  179.             personHit = false;
  180.             for (int i = 0; i < blocks.Count; i++)
  181.             {
  182.                 // Animate this block falling
  183.                 blocks[i].Position += new Vector2(0.0f, BlockFallSpeed);
  184.                 blocks[i].Rotation += BlockRotateSpeed;
  185.  
  186.                 // Build the block's transform
  187.                 Matrix blockTransform =
  188.                     Matrix.CreateTranslation(new Vector3(-blockOrigin, 0.0f)) *
  189.                     // Matrix.CreateScale(block.Scale) *  would go here
  190.                     Matrix.CreateRotationZ(blocks[i].Rotation) *
  191.                     Matrix.CreateTranslation(new Vector3(blocks[i].Position, 0.0f));
  192.  
  193.                 // Calculate the bounding rectangle of this block in world space
  194.                 Rectangle blockRectangle = CalculateBoundingRectangle(
  195.                          new Rectangle(0, 0, blockTexture.Width, blockTexture.Height),
  196.                          blockTransform);
  197.  
  198.                 // The per-pixel check is expensive, so check the bounding rectangles
  199.                 // first to prevent testing pixels when collisions are impossible.
  200.                 if (personRectangle.Intersects(blockRectangle))
  201.                 {
  202.                     // Check collision with person
  203.                     if (IntersectPixels(personTransform, personTexture.Width,
  204.                                         personTexture.Height, personTextureData,
  205.                                         blockTransform, blockTexture.Width,
  206.                                         blockTexture.Height, blockTextureData))
  207.                     {
  208.                         personHit = true;
  209.                     }
  210.                 }
  211.  
  212.                 // Remove this block if it have fallen off the screen
  213.                 if (blocks[i].Position.Y >
  214.                     Window.ClientBounds.Height + blockOrigin.Length())
  215.                 {
  216.                     blocks.RemoveAt(i);
  217.  
  218.                     // When removing a block, the next block will have the same index
  219.                     // as the current block. Decrement i to prevent skipping a block.
  220.                     i--;
  221.                 }
  222.             }
  223.  
  224.             base.Update(gameTime);
  225.         }
  226.  
  227.  
  228.         /// <summary>
  229.         /// This is called when the game should draw itself.
  230.         /// </summary>
  231.         /// <param name="gameTime">Provides a snapshot of timing values.</param>
  232.         protected override void Draw(GameTime gameTime)
  233.         {
  234.             GraphicsDevice device = graphics.GraphicsDevice;
  235.  
  236.             // Change the background to red when the person was hit by a block
  237.             if (personHit)
  238.             {
  239.                 device.Clear(Color.Red);
  240.             }
  241.             else
  242.             {
  243.                 device.Clear(Color.CornflowerBlue);
  244.             }
  245.  
  246.  
  247.             spriteBatch.Begin();
  248.  
  249.             // Draw person
  250.             spriteBatch.Draw(personTexture, personPosition, Color.White);
  251.  
  252.             // Draw blocks
  253.             foreach (Block block in blocks)
  254.             {
  255.                 spriteBatch.Draw(blockTexture, block.Position, null, Color.White,
  256.                     block.Rotation, blockOrigin, 1.0f, SpriteEffects.None, 0.0f);
  257.             }
  258.  
  259.             spriteBatch.End();
  260.  
  261.  
  262.             base.Draw(gameTime);
  263.         }
  264.  
  265.  
  266.         /// <summary>
  267.         /// Determines if there is overlap of the non-transparent pixels
  268.         /// between two sprites.
  269.         /// </summary>
  270.         /// <param name="rectangleA">Bounding rectangle of the first sprite</param>
  271.         /// <param name="dataA">Pixel data of the first sprite</param>
  272.         /// <param name="rectangleB">Bouding rectangle of the second sprite</param>
  273.         /// <param name="dataB">Pixel data of the second sprite</param>
  274.         /// <returns>True if non-transparent pixels overlap; false otherwise</returns>
  275.         public static bool IntersectPixels(Rectangle rectangleA, Color[] dataA,
  276.                                            Rectangle rectangleB, Color[] dataB)
  277.         {
  278.             // Find the bounds of the rectangle intersection
  279.             int top = Math.Max(rectangleA.Top, rectangleB.Top);
  280.             int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
  281.             int left = Math.Max(rectangleA.Left, rectangleB.Left);
  282.             int right = Math.Min(rectangleA.Right, rectangleB.Right);
  283.  
  284.             // Check every point within the intersection bounds
  285.             for (int y = top; y < bottom; y++)
  286.             {
  287.                 for (int x = left; x < right; x++)
  288.                 {
  289.                     // Get the color of both pixels at this point
  290.                     Color colorA = dataA[(x - rectangleA.Left) +
  291.                                          (y - rectangleA.Top) * rectangleA.Width];
  292.                     Color colorB = dataB[(x - rectangleB.Left) +
  293.                                          (y - rectangleB.Top) * rectangleB.Width];
  294.  
  295.                     // If both pixels are not completely transparent,
  296.                     if (colorA.A != 0 && colorB.A != 0)
  297.                     {
  298.                         // then an intersection has been found
  299.                         return true;
  300.                     }
  301.                 }
  302.             }
  303.  
  304.             // No intersection found
  305.             return false;
  306.         }
  307.  
  308.  
  309.         /// <summary>
  310.         /// Determines if there is overlap of the non-transparent pixels between two
  311.         /// sprites.
  312.         /// </summary>
  313.         /// <param name="transformA">World transform of the first sprite.</param>
  314.         /// <param name="widthA">Width of the first sprite's texture.</param>
  315.         /// <param name="heightA">Height of the first sprite's texture.</param>
  316.         /// <param name="dataA">Pixel color data of the first sprite.</param>
  317.         /// <param name="transformB">World transform of the second sprite.</param>
  318.         /// <param name="widthB">Width of the second sprite's texture.</param>
  319.         /// <param name="heightB">Height of the second sprite's texture.</param>
  320.         /// <param name="dataB">Pixel color data of the second sprite.</param>
  321.         /// <returns>True if non-transparent pixels overlap; false otherwise</returns>
  322.         public static bool IntersectPixels(
  323.                             Matrix transformA, int widthA, int heightA, Color[] dataA,
  324.                             Matrix transformB, int widthB, int heightB, Color[] dataB)
  325.         {
  326.             // Calculate a matrix which transforms from A's local space into
  327.             // world space and then into B's local space
  328.             Matrix transformAToB = transformA * Matrix.Invert(transformB);
  329.  
  330.             // When a point moves in A's local space, it moves in B's local space with a
  331.             // fixed direction and distance proportional to the movement in A.
  332.             // This algorithm steps through A one pixel at a time along A's X and Y axes
  333.             // Calculate the analogous steps in B:
  334.             Vector2 stepX = Vector2.TransformNormal(Vector2.UnitX, transformAToB);
  335.             Vector2 stepY = Vector2.TransformNormal(Vector2.UnitY, transformAToB);
  336.  
  337.             // Calculate the top left corner of A in B's local space
  338.             // This variable will be reused to keep track of the start of each row
  339.             Vector2 yPosInB = Vector2.Transform(Vector2.Zero, transformAToB);
  340.  
  341.             // For each row of pixels in A
  342.             for (int yA = 0; yA < heightA; yA++)
  343.             {
  344.                 // Start at the beginning of the row
  345.                 Vector2 posInB = yPosInB;
  346.  
  347.                 // For each pixel in this row
  348.                 for (int xA = 0; xA < widthA; xA++)
  349.                 {
  350.                     // Round to the nearest pixel
  351.                     int xB = (int)Math.Round(posInB.X);
  352.                     int yB = (int)Math.Round(posInB.Y);
  353.  
  354.                     // If the pixel lies within the bounds of B
  355.                     if (0 <= xB && xB < widthB &&
  356.                         0 <= yB && yB < heightB)
  357.                     {
  358.                         // Get the colors of the overlapping pixels
  359.                         Color colorA = dataA[xA + yA * widthA];
  360.                         Color colorB = dataB[xB + yB * widthB];
  361.  
  362.                         // If both pixels are not completely transparent,
  363.                         if (colorA.A != 0 && colorB.A != 0)
  364.                         {
  365.                             // then an intersection has been found
  366.                             return true;
  367.                         }
  368.                     }
  369.  
  370.                     // Move to the next pixel in the row
  371.                     posInB += stepX;
  372.                 }
  373.  
  374.                 // Move to the next row
  375.                 yPosInB += stepY;
  376.             }
  377.  
  378.             // No intersection found
  379.             return false;
  380.         }
  381.  
  382.  
  383.         /// <summary>
  384.         /// Calculates an axis aligned rectangle which fully contains an arbitrarily
  385.         /// transformed axis aligned rectangle.
  386.         /// </summary>
  387.         /// <param name="rectangle">Original bounding rectangle.</param>
  388.         /// <param name="transform">World transform of the rectangle.</param>
  389.         /// <returns>A new rectangle which contains the trasnformed rectangle.</returns>
  390.         public static Rectangle CalculateBoundingRectangle(Rectangle rectangle,
  391.                                                            Matrix transform)
  392.         {
  393.             // Get all four corners in local space
  394.             Vector2 leftTop = new Vector2(rectangle.Left, rectangle.Top);
  395.             Vector2 rightTop = new Vector2(rectangle.Right, rectangle.Top);
  396.             Vector2 leftBottom = new Vector2(rectangle.Left, rectangle.Bottom);
  397.             Vector2 rightBottom = new Vector2(rectangle.Right, rectangle.Bottom);
  398.  
  399.             // Transform all four corners into work space
  400.             Vector2.Transform(ref leftTop, ref transform, out leftTop);
  401.             Vector2.Transform(ref rightTop, ref transform, out rightTop);
  402.             Vector2.Transform(ref leftBottom, ref transform, out leftBottom);
  403.             Vector2.Transform(ref rightBottom, ref transform, out rightBottom);
  404.  
  405.             // Find the minimum and maximum extents of the rectangle in world space
  406.             Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
  407.                                       Vector2.Min(leftBottom, rightBottom));
  408.             Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
  409.                                       Vector2.Max(leftBottom, rightBottom));
  410.  
  411.             // Return that as a rectangle
  412.             return new Rectangle((int)min.X, (int)min.Y,
  413.                                  (int)(max.X - min.X), (int)(max.Y - min.Y));
  414.         }
  415.     }
  416. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement