像素完美碰撞导致与迷宫和PacMan的不准确碰撞

时间:2011-09-17 22:02:21

标签: c# xna collision-detection xna-4.0 pixel-perfect

我正在创建一个PacMan克隆,只是为了好玩,我使用像素完美碰撞算法来检测PacMan何时与迷宫和点碰撞(尚未在游戏中实现)。我知道我正在使用算法,但是当迷宫和PacMan都没有彼此靠近时,PacMan会与迷宫相撞。我拍照显示调试屏幕和游戏。这是下面的图像和代码: Proof 主要代码:

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类:

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。 PacMan精灵有一个透明的背景,你在迷宫上看到的黑色区域是透明的。所以我知道这不是问题所在。顺便说一句对不起这么长的帖子。 修改:添加评论以使代码更易于理解。

2 个答案:

答案 0 :(得分:2)

您的问题是您尝试使用此行测试“不完全透明”:

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

但事实恰恰相反:那就是测试它们是否完全不合适。

完全透明将是alpha 255或1.0f,具体取决于int / float。

答案 1 :(得分:0)

在做了一些研究之后,我发现做瓦片底座碰撞比用像素完美碰撞更好。