Question

I am creating a PacMan clone just for fun and I am using the pixel perfect collision algorithm to detect when PacMan collides with the maze and the dots (which has not been implemented in the game yet). I know I am using the algorithm correctly but the PacMan is collided with the maze when both the maze and PacMan and are not near each other. I picture for proof showing the debugging screen and the game. Here is the image and code below: Proof Main Code:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace PacMan_Bytes
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        enum Direction
        {
            Left,
            Right,
            Up,
            Down
        };
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D maze;
        Texture2D pacman;
        Color[] mazeclr = new Color[484 * 483];
        Color[] pacmanclr = new Color[20 * 27];
        Rectangle dest;
        Rectangle src;
        int frame = 2;
        float rotation = 0.0f;
        Direction dir;
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            graphics.PreferredBackBufferWidth = 484;
            graphics.PreferredBackBufferHeight = 483;
            dest = new Rectangle(50, 455, 20, 27);
            src = new Rectangle(frame, 0, 20, 27);
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            maze = Content.Load<Texture2D>("maze");
            pacman = Content.Load<Texture2D>("pacman");
            maze.GetData<Color>(mazeclr);
           // Get the colors that comprises the image for pixel  perfect collision.
            pacman.GetData<Color>(0, src, pacmanclr, 0, 20 * 27);
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();
            this.TargetElapsedTime = TimeSpan.FromSeconds(1.0f / 13.0f);
            // Each frame check if the PacMan has collided with the maze.
            bool collision = PacmanAnimator.CollidedWithMaze(dest, pacmanclr, maze.Bounds, mazeclr);
            // If PacMan doesn't collide with the maze allow the PacMan to move.
            if (collision == false)
            {
                if (Keyboard.GetState().IsKeyDown(Keys.Left))
                {

                    PacmanAnimator.AnimateAndMoveLeft(ref rotation, ref frame, ref src, ref dest);
                    dir = Direction.Left;
                }
                else if (Keyboard.GetState().IsKeyDown(Keys.Right))
                {

                    PacmanAnimator.AnimateAndMoveRight(ref rotation, ref frame, ref src, ref dest);
                    dir = Direction.Right;
                }
                else if (Keyboard.GetState().IsKeyDown(Keys.Up))
                {

                    PacmanAnimator.AnimateAndMoveUp(ref rotation, ref frame, ref src, ref dest);
                    dir = Direction.Up;
                }
                else if (Keyboard.GetState().IsKeyDown(Keys.Down))
                {

                    PacmanAnimator.AnimateAndMoveDown(ref rotation, ref frame, ref src, ref dest);
                    dir = Direction.Down;
                }
            }
            // If collision is true move the PacMan away from the maze's border.
            else if (collision == true)
            {
                if (dir == Direction.Down)
                {
                    dest.Y -= 1;
                }
                else if (dir == Direction.Up)
                {
                    dest.Y += 1;
                }
                else if (dir == Direction.Left)
                {
                    dest.X += 1;
                }
                else if (dir == Direction.Right)
                {
                    dest.X -= 1;
                }
            }
            if (dest.X < 0)
            {
                dest.X = graphics.PreferredBackBufferWidth - 20;
            }
            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Transparent);

            spriteBatch.Begin();
            spriteBatch.Draw(maze, Vector2.Zero, Color.White);
            spriteBatch.Draw(pacman, dest, src, Color.White, rotation, new Vector2(20 / 2, 24 / 2), SpriteEffects.None, 1);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

PacmanAnimator Class:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace PacMan_Bytes
{
    static class PacmanAnimator
    {
        // Animates and moves the PacMan to the left.
        public static void AnimateAndMoveLeft(ref float rotation, ref int frame, ref Rectangle src, ref Rectangle dest)
        {
            switch (frame)
            {
                case 2:
                    {
                        frame = 40;
                        break;
                    }
                case 40:
                    {
                        frame = 2;
                        break;
                    }
            }
            src.X = frame;
            dest.X -= 7;
            rotation = MathHelper.ToRadians(180.0f);
        }
        // Animates and moves the sprite to the right.
        public static void AnimateAndMoveRight(ref float rotation, ref int frame, ref Rectangle src, ref Rectangle dest)
        {
            switch (frame)
            {
                case 2:
                    {
                        frame = 40;
                        break;
                    }
                case 40:
                    {
                        frame = 2;
                        break;
                    }
            }
            src.X = frame;
            dest.X += 7;
            rotation = 0.0f;
        }
        // Moves and animates the sprite upward.
        public static void AnimateAndMoveUp(ref float rotation, ref int frame, ref Rectangle src, ref Rectangle dest)
        {
            switch (frame)
            {
                case 2:
                    {
                        frame = 40;
                        break;
                    }
                case 40:
                    {
                        frame = 2;
                        break;
                    }
            }
            src.X = frame;
            dest.Y -= 7;
            rotation = MathHelper.ToRadians(270.0f);
        }
        // Moves the sprite downward and animates it.
        public static void AnimateAndMoveDown(ref float rotation, ref int frame, ref Rectangle src, ref Rectangle dest)
        {
            switch (frame)
            {
                case 2:
                    {
                        frame = 40;
                        break;
                    }
                case 40:
                    {
                        frame = 2;
                        break;
                    }
            }
            src.X = frame;
            dest.Y += 7;
            rotation = MathHelper.ToRadians(90.0f);
        }
        // Subroutine that deals with collision.
        public static bool CollidedWithMaze(Rectangle rectangleA, Color[] dataA, Rectangle rectangleB, Color[] dataB)
        {
            // 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);

            // 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;
                    }
                }
            }

            // No intersection found
            return false;
        }
    }
}

PS. The PacMan sprite has a transparent background and the black areas you see on the maze are transparent. So I know that is not the problem. By the way sorry for such a long post. Edit: Added comments to make code more understandable.

Was it helpful?

Solution 2

After doing some research I found out it is better to do tile base collision rather than pixel perfect collision.

OTHER TIPS

Your problem is that you're trying to test "not completely transparent" with this line:

// If both pixels are not completely transparent,
if (colorA.A != 0 && colorB.A != 0)

But that's the opposite: that is testing if they're NOT completely OPAQUE.

Completely transparent would be alpha 255, or 1.0f, depending on int/float.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top