XNA-2D碰撞-使用像素偵測



上一篇使用XNA-2D碰撞-使用矩形偵測的缺點就是對於不規則的物體沒輒!而遊戲裡常常都是不規則的物體,所以我們來試試看用像素偵測的方式,

如此不論圖形長什麼樣子都可以準確的偵測出來。

首先我們要將會碰撞的物體其背景變成透明的,png檔案有支援透明背景,我們就用png圖檔來試驗!

像素碰撞的想法如下

未碰撞:

 

未命名 -4

碰撞:

 

 未命名 -6

其實還是要有一個碰撞矩形,而當此矩形發生碰撞後,取得重疊的區域,再檢查此區域內兩張圖的每一個像素,若出現有一個點是兩張圖都不透明,則發生碰撞。

因為要取得圖形的顏色,我們在有一個Color[]的陣列,並在初始化的時候將圖形的顏色資訊填入,片段程式碼如下:

1protected Color[] pixelColors;
2 
3public IPixelCollideObject(Texture2D image)
4    : base(image) {
5    collideRectangle =new Rectangle((int)position.X, (int)position.Y, image.Width, image.Height);
6    pixelColors =new Color[image.Width * image.Height];
7    image.GetData<Color>(pixelColors);
8}

Texture2D.GetData可以讓我們取得圖片的資訊。

接著就是主要的判段程式碼:

 

01public bool CollideCheck(ICollideable obj) {
02    if(collodeable) {
03        PixelCollideObject o = objas PixelCollideObject;
04        if(o != null) {
05            Rectangle intersect = Rectangle.Intersect(collideRectangle, o.collideRectangle);
06            if(!intersect.IsEmpty) {
07                for(int y = intersect.Top; y < intersect.Bottom; y++) {
08                    for(int x = intersect.Left; x < intersect.Right; x++) {
09                        Color colorA = pixelColors[(x - collideRectangle.Left) + (y - collideRectangle.Top) * collideRectangle.Width];
10                        Color colorB = o.pixelColors[(x - o.collideRectangle.Left) + (y - o.collideRectangle.Top) * o.collideRectangle.Width];
11                        if(colorA.A != 0 && colorB.A != 0) returntrue;
12                    }
13                }
14            }
15        }
16    }
17    returnfalse;
18}

取得重疊區域使用Rectangle.Intersect函式,他是一個靜態的函式,傳入兩個矩形,將重疊部分回傳,若無重疊則回傳Rectangle.Empty。

迴圈則是由左而又由上而下檢查每個像素,若兩張圖在重疊區域有都不是透明的點的話,就表示發生碰撞了!

範例下載:WindowsGame1.rar




XNA-像素碰撞Per-Pixel Collision(程式範例)

    繼續上次矩形碰撞Rectangle Collision的範例程式,將它修改成使用像素偵測的方法去檢驗是否有碰撞到,這個碰撞的方法會比內建的矩形碰撞較準確,至於它大致做的方法在之前的文章如何實現碰撞偵測?有提到過,主要參考到XNA開發者俱樂部的Collision Series 2: 2D Per-Pixel Collision

    此範例也是運用鍵盤的上、下、左、右鍵來移動人物圖片,最主要就是看它與磚塊圖片碰撞的地方,會比矩形偵測來的準確。

程式範例:

001using System;
002using System.Collections.Generic;
003using System.Linq;
004using Microsoft.Xna.Framework;
005using Microsoft.Xna.Framework.Audio;
006using Microsoft.Xna.Framework.Content;
007using Microsoft.Xna.Framework.GamerServices;
008using Microsoft.Xna.Framework.Graphics;
009using Microsoft.Xna.Framework.Input;
010using Microsoft.Xna.Framework.Media;
011using Microsoft.Xna.Framework.Net;
012using Microsoft.Xna.Framework.Storage;
013 
014namespace Per_Pixel_Collision
015{
016    public class Game1 : Microsoft.Xna.Framework.Game
017    {
018        GraphicsDeviceManager graphics;
019        SpriteBatch spriteBatch;
020        Texture2D block;
021        Texture2D person;
022        Vector2 blockpos;
023        Vector2 personpos;
024        Color[] blockTextureData;
025        Color[] personTextureData;
026        int movespeed = 3;
027        bool personHit = false;
028 
029        public Game1()
030        {
031            graphics = new GraphicsDeviceManager(this);
032            Content.RootDirectory = "Content";
033        }
034 
035        protected override void Initialize()
036        {
037            blockpos = new Vector2(0, 0);
038            personpos = new Vector2(50, 0);
039            base.Initialize();
040        }
041 
042        protected override void LoadContent()
043        {
044            spriteBatch = new SpriteBatch(GraphicsDevice);
045            spriteBatch = new SpriteBatch(GraphicsDevice);
046            block = Content.Load<Texture2D>("Block");
047            blockTextureData = new Color[block.Width * block.Height];
048            block.GetData(blockTextureData);
049            person = Content.Load<Texture2D>("Person");
050            personTextureData = new Color[person.Width * person.Height];
051            person.GetData(personTextureData);
052        }
053 
054        protected override void UnloadContent()
055        {
056        }
057 
058        protected override void Update(GameTime gameTime)
059        {
060            KeyboardState keyboard = Keyboard.GetState();
061            if (keyboard.IsKeyDown(Keys.Up))
062            {
063                personpos = new Vector2(personpos.X, personpos.Y - movespeed);
064            }
065            if (keyboard.IsKeyDown(Keys.Down))
066            {
067                personpos = new Vector2(personpos.X, personpos.Y + movespeed);
068            }
069            if (keyboard.IsKeyDown(Keys.Left))
070            {
071                personpos = new Vector2(personpos.X - movespeed, personpos.Y);
072            }
073            if (keyboard.IsKeyDown(Keys.Right))
074            {
075                personpos = new Vector2(personpos.X + movespeed, personpos.Y);
076            }
077            Rectangle personRectangle = new Rectangle((int)personpos.X, (int)personpos.Y, person.Width, person.Height);
078            Rectangle blockRectangle = new Rectangle((int)blockpos.X, (int)blockpos.Y, block.Width, block.Height);
079            if (IntersectPixels(personRectangle,personTextureData,blockRectangle, blockTextureData ))
080            {
081                personHit = true;
082            }
083            else
084            {
085                personHit = false;
086            }
087            base.Update(gameTime);
088        }
089 
090        protected override void Draw(GameTime gameTime)
091        {
092            if (personHit == true)
093                GraphicsDevice.Clear(Color.Red);
094            else
095                GraphicsDevice.Clear(Color.CornflowerBlue);
096            spriteBatch.Begin();
097            spriteBatch.Draw(block, blockpos, Color.White);
098            spriteBatch.Draw(person, personpos, Color.White);
099            spriteBatch.End();
100            base.Draw(gameTime);
101        }
102 
103        public bool IntersectPixels(Rectangle rectangleA, Color[] dataA, Rectangle rectangleB, Color[] dataB)
104        {
105            int top = Math.Max(rectangleA.Top, rectangleB.Top);
106            int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
107            int left = Math.Max(rectangleA.Left, rectangleB.Left);
108            int right = Math.Min(rectangleA.Right, rectangleB.Right);
109 
110            for (int y = top; y < bottom; y++)
111            {
112                for (int x = left; x < right; x++)
113                {
114                    Color colorA = dataA[(x - rectangleA.Left) + (y - rectangleA.Top) * rectangleA.Width];
115                    Color colorB = dataB[(x - rectangleB.Left) + (y - rectangleB.Top) * rectangleB.Width];
116                    if ((colorA.A != 0 ) && (colorB.A != 0))
117                    {
118                        return true;
119                    }
120                }
121            }
122            return false;
123        }
124    }
125}


    24~25行宣告的變數是用來存放兩圖片RGBA的資訊。

    47~48行和50~51行運用讀取圖片完的寬和高來決定存放該圖所需要的陣列大小,並且取出兩圖片RGBA的資訊。

    79行判斷是否碰撞到的函數IntersectPixels需要傳入的參數為兩圖片的Rectange型態和兩圖片Color型態的資訊。

    105~108行則是找出兩圖片交集的矩形範圍是多少乘多少。

    110行迴圈最主要檢查交集圖片中每個點是否有碰撞到。

    114~115行得到交集圖片各點的RGBA的資料。

    116行運用圖片的alpha值來判斷是否有碰撞到,如果交集圖片的某點alpha值都不是0那就代表碰到了。

1

這樣表示沒有碰撞到

這樣才表示有碰撞到

    122行交集圖片沒有碰撞在一起的點,所以回傳false。

最後附上專案檔Per-Pixel Collision.rar


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值