XNA-像素碰撞Per-Pixel Collision(程式範例)
繼續上次矩形碰撞Rectangle Collision的範例程式,將它修改成使用像素偵測的方法去檢驗是否有碰撞到,這個碰撞的方法會比內建的矩形碰撞較準確,至於它大致做的方法在之前的文章如何實現碰撞偵測?有提到過,主要參考到XNA開發者俱樂部的Collision Series 2: 2D Per-Pixel Collision。
此範例也是運用鍵盤的上、下、左、右鍵來移動人物圖片,最主要就是看它與磚塊圖片碰撞的地方,會比矩形偵測來的準確。
程式範例:
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;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace Per_Pixel_Collision
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D block;
Texture2D person;
Vector2 blockpos;
Vector2 personpos;
Color[] blockTextureData;
Color[] personTextureData;
int movespeed = 3;
bool personHit = false;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
blockpos = new Vector2(0, 0);
personpos = new Vector2(50, 0);
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
spriteBatch = new SpriteBatch(GraphicsDevice);
block = Content.Load<Texture2D>("Block");
blockTextureData = new Color[block.Width * block.Height];
block.GetData(blockTextureData);
person = Content.Load<Texture2D>("Person");
personTextureData = new Color[person.Width * person.Height];
person.GetData(personTextureData);
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
KeyboardState keyboard = Keyboard.GetState();
if (keyboard.IsKeyDown(Keys.Up))
{
personpos = new Vector2(personpos.X, personpos.Y - movespeed);
}
if (keyboard.IsKeyDown(Keys.Down))
{
personpos = new Vector2(personpos.X, personpos.Y + movespeed);
}
if (keyboard.IsKeyDown(Keys.Left))
{
personpos = new Vector2(personpos.X - movespeed, personpos.Y);
}
if (keyboard.IsKeyDown(Keys.Right))
{
personpos = new Vector2(personpos.X + movespeed, personpos.Y);
}
Rectangle personRectangle = new Rectangle((int)personpos.X, (int)personpos.Y, person.Width, person.Height);
Rectangle blockRectangle = new Rectangle((int)blockpos.X, (int)blockpos.Y, block.Width, block.Height);
if (IntersectPixels(personRectangle,personTextureData,blockRectangle, blockTextureData ))
{
personHit = true;
}
else
{
personHit = false;
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
if (personHit == true)
GraphicsDevice.Clear(Color.Red);
else
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(block, blockpos, Color.White);
spriteBatch.Draw(person, personpos, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
public bool IntersectPixels(Rectangle rectangleA, Color[] dataA, Rectangle rectangleB, Color[] dataB)
{
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);
for (int y = top; y < bottom; y++)
{
for (int x = left; x < right; x++)
{
Color colorA = dataA[(x - rectangleA.Left) + (y - rectangleA.Top) * rectangleA.Width];
Color colorB = dataB[(x - rectangleB.Left) + (y - rectangleB.Top) * rectangleB.Width];
if ((colorA.A != 0 ) && (colorB.A != 0))
{
return true;
}
}
}
return false;
}
}
}
24~25行宣告的變數是用來存放兩圖片RGBA的資訊。
47~48行和50~51行運用讀取圖片完的寬和高來決定存放該圖所需要的陣列大小,並且取出兩圖片RGBA的資訊。
79行判斷是否碰撞到的函數IntersectPixels需要傳入的參數為兩圖片的Rectange型態和兩圖片Color型態的資訊。
105~108行則是找出兩圖片交集的矩形範圍是多少乘多少。
110行迴圈最主要檢查交集圖片中每個點是否有碰撞到。
114~115行得到交集圖片各點的RGBA的資料。
116行運用圖片的alpha值來判斷是否有碰撞到,如果交集圖片的某點alpha值都不是0那就代表碰到了。
這樣表示沒有碰撞到
這樣才表示有碰撞到
122行交集圖片沒有碰撞在一起的點,所以回傳false。
最後附上專案檔Per-Pixel Collision.rar