Basic Per Pixel Collision

From TRCCompSci - AQA Computer Science
Revision as of 12:10, 15 September 2017 by Admin (talk | contribs)
Jump to: navigation, search

Introduction

This will build on the rectangle bounds collision method. Below is an example of per pixel collision:

Per pixel.gif

Characters

I have declared the following Textures & 2 vectors to control the position of these. I have also added two Color arrays to store the colours within the player & enemy:

        Texture2D enemy;
        Texture2D player;
        Vector2 ppos, epos;
        Color[] playerTextureData;
        Color[] enemyTextureData;

In reality your project will probably create a class for player and a class for enemy. This will then include the texture, position and so on for your character. For simplicity and for the fear of giving you a complete player class definition, or to give you a complete collision detection method, I will show you how to check for a collision between two object by creating bounding rectangles and checking for an intersection.

In the Initialize method for the game I have set the position vectors for my player and enemy:

            ppos = new Vector2(0, 0);
            epos = new Vector2(300, 300);
            // Set a constant player move speed
            playerMoveSpeed = 8.0f;

LoadContent Section

You will need to add the following lines to the LoadContent section:

           // Extract collision data
            enemyTextureData = new Color[enemy.Width * enemy.Height];
            enemy.GetData(enemyTextureData);
            playerTextureData = new Color[player.Width * player.Height];
            player.GetData(playerTextureData);

The code above will create an array of the colour of every pixel in both textures. The enemyTextureData is first given a size, and then the GetData method is used to populate the array with the appropriate values.

New Method

Create a new method in your Game1.cs:

static bool IntersectPixels(Rectangle rectangleA, Color[] dataA, Rectangle rectangleB, Color[] dataB)

In the new method add the following lines:

            // Find the bounds of the rectangle intersection
            int top = Math.Max(rectangleA.Top, rectangleB.Top);
            int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
            int left = Math.Max(rectangleA.Left, rectangleB.Left);
            int right = Math.Min(rectangleA.Right, rectangleB.Right);

top, bottom, left, and right will create a rectangle of the intersecting textures. What we now need to do is to check the colour data for this intersecting rectangle. If either colours are transparent then it is not a collision, but if both the player texture colour and the enemy texture colour aren't transparent then a collision has occurred:


            // Check every point within the intersection bounds
            for (int y = top; y < bottom; y++)
            {
                for (int x = left; x < right; x++)
                {
                    // Get the color of both pixels at this point
                    Color colorA = dataA[(x - rectangleA.Left) + (y - rectangleA.Top) * rectangleA.Width];
                    Color colorB = dataB[(x - rectangleB.Left) + (y - rectangleB.Top) * rectangleB.Width];

                    // If both pixels are not completely transparent,
                    if (colorA.A != 0 && colorB.A != 0)
                    {
                        // then an intersection has been found
                        return true;
                    }
                }
            }

Player Movement

I have already created the basic player movement:

            if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
                ButtonState.Pressed || Keyboard.GetState().IsKeyDown(
                Keys.Escape))
                Exit();

            // Save the previous state of the keyboardso we can determine single key presses
            previousKeyboardState = currentKeyboardState;

            // Read the current state of the keyboard and store it
            currentKeyboardState = Keyboard.GetState();

            if (currentKeyboardState.IsKeyDown(Keys.Left))
            {
                ppos.X -= playerMoveSpeed;
            }

            if (currentKeyboardState.IsKeyDown(Keys.Right))
            {
                ppos.X += playerMoveSpeed;
            }

            if (currentKeyboardState.IsKeyDown(Keys.Up))
            {
                ppos.Y -= playerMoveSpeed;
            }

            if (currentKeyboardState.IsKeyDown(Keys.Down))
            {
                ppos.Y += playerMoveSpeed;
            }

This is from the Update method. It will allow your player to move around, you can't have collisions if you can't move.

Create Player Rectangle

// Get bounds of enemy
            Rectangle playerRectangle = new Rectangle((int)ppos.X, (int)ppos.Y, player.Width, player.Height);

The (int) is an example of casting, this will convert the value ppos.X to an integer. The rectangle points are its X & Y (ie top right corner), and then the width and height are used to get the bottom left corner.

Create Enemy Rectangle

// Get bounds of enemy
            Rectangle enemyRectangle = new Rectangle((int)epos.X, (int)epos.Y, enemy.Width, enemy.Height);

This is the same as above except using the enemy.

Checking if they overlap

Now we have the 2 rectangles you can use these and the colour data to check for an intersection. This use the new method you created above:

// Get bounds of enemy
if (IntersectPixels(playerRectangle, playerTextureData, enemyRectangle, enemyTextureData))
            {
                 A COLLISION HAS TAKEN PLACE AND YOUR PROGRAM SHOULD DO SOMETHING
            }

My example altered the movement values changed before the collision detection:

 if (IntersectPixels(playerRectangle, playerTextureData, enemyRectangle, enemyTextureData))
            {
                 if (currentKeyboardState.IsKeyDown(Keys.Left))
                {
                    ppos.X += playerMoveSpeed;
                }

                if (currentKeyboardState.IsKeyDown(Keys.Right))
                {
                    ppos.X -= playerMoveSpeed;
                }

                if (currentKeyboardState.IsKeyDown(Keys.Up))
                {
                    ppos.Y += playerMoveSpeed;
                }

                if (currentKeyboardState.IsKeyDown(Keys.Down))
                {
                    ppos.Y -= playerMoveSpeed;
                }
            }