Pixel Perfect Collision вызывает неточное столкновение с Maze и PacMan - PullRequest
0 голосов
/ 18 сентября 2011

Я создаю клон 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 ]

2 голосов
/ 18 сентября 2011

Ваша проблема в том, что вы пытаетесь проверить "не полностью прозрачный" с помощью этой строки:

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

Но это наоборот: это тестирование, если они НЕ полностью непрозрачны.

Полностью прозрачным будет альфа 255 или 1,0f, в зависимости от типа int / float.

0 голосов
/ 18 сентября 2011

Проведя некоторые исследования, я обнаружил, что лучше делать столкновение с плиткой, а не с точностью до пикселя.

...