2011-09-17 6 views
0

PacMan 복제본을 재미있게 만들기 위해 픽셀 완벽한 충돌 알고리즘을 사용하여 PacMan이 미로와 도트 (충돌하지 않음)에 충돌했을 때이를 감지합니다. 아직 게임이다). 알고리즘을 올바르게 사용하고 있지만 PacMan은 미로와 PacMan이 서로 가깝지 않을 때 미로와 충돌합니다. 디버깅 화면과 게임을 보여주는 증거물을 보여줍니다. Proof 홈페이지 코드 :Pixel Perfect Collision이 미로와 PacMan과 부정확 한 충돌을 야기 함

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 스프라이트는 투명한 배경을 가지고 있으며 미로에서 볼 수있는 검은 영역은 투명합니다. 그래서 나는 그것이 문제가 아니라는 것을 안다. 그런 긴 게시물에 대해 유감입니다. 편집 : 코드를 이해하기 쉽게 추가했습니다.

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

하지만 그 반대입니다 :

+0

코드 단편을 단순화하십시오. –

+1

흠 ... 나는 이것을하기 위해 완벽한 픽셀 충돌이 필요 없다고 생각합니다 ... 팩맨은 움직임을 제한했습니다 ...지도의 블록을 나타내는 간단한 2D 배열로 훨씬 쉽게 작업 할 수 있습니다. 충돌 만 확인하면됩니다. 팩맨이 블록의 중심에있을 때. – Blau

답변

0

일부 연구를 한 후에 픽셀 완전 충돌보다는 타일 기반 충돌을 수행하는 것이 더 나은 것으로 밝혀졌습니다.

2

귀하의 문제는 당신이이 라인에 "완전히 투명하지"를 테스트하기 위해 노력하고 있다는 점이다 그들이 완전히 불투명하지 않는 경우 그 시험하고있다.

완전히 투명하면 int/float에 따라 알파 255 또는 1.0f가됩니다.

+0

그러나 Blau와 동의하는 경향이 있습니다. 픽셀 완벽한 방법은이 방법이 아닙니다. 그러나 당신이 방금 어떤 이유에서든 그것을 시도하고 싶다면 그것은 멋집니다. –

관련 문제