XNA - 2D碰撞-使用像素偵測,趴特兔!

  • 6506
  • 0
  • XNA
  • 2010-08-28

XNA - 2D碰撞-使用像素偵測,趴特兔!

當我們的2D圖會旋轉的話,該怎麼碰撞呢?

2D碰撞-使用像素偵測 這篇說明了像素碰撞的檢查方法,
但是當圖形有發生旋轉的話,這個方法就不能用了。
要偵測旋轉的圖形,我們也需要跟著旋轉!
基本上,偵測的原理和之前那一篇一樣,檢查重疊的部份的透明度,
但是因為圖片顯示在螢幕上有旋轉,但是我們原始的圖片卻沒有(也不能)旋轉,
所以要如何將螢幕上已經旋轉的圖片的某些點,找到原始圖片相對應的點,就是這裡要克服的問題。
首先,算出轉換矩陣,由於矩陣運算是以三維座標為主,所以我們的z軸就給零。

   1: transform = Matrix.CreateTranslation(
   2: new Vector3(-image.Origin, 0.0f)) *
   3: Matrix.CreateRotationZ(image.Rotation) *
   4: Matrix.CreateTranslation(new Vector3(image.Position, 0.0f));

轉換矩陣的算法是先將圖形中心點移回(0,0),再沿著z軸旋轉、再移動到圖形的螢幕座標。
此矩陣就意味著原始圖形的每個像素座標乘上此矩陣,就會變成顯示在螢幕上的座標。

pic1

接著,因為圖形有旋轉,所以碰撞矩型也要跟著改變,
先求出原始圖形的四個頂點座標,在分別乘上轉換矩陣,得出顯示在螢幕上實際的四個頂點座標
取最大的值,當做新的碰撞矩型,程式碼如下:

   1: Vector2 leftTop = new Vector2(0, 0);
   2: Vector2 rightTop = new Vector2(image.Width, 0);
   3: Vector2 leftBottom = new Vector2(0, image.Height);
   4: Vector2 rightBottom = new Vector2(image.Width, image.Height);
   5:  
   6: Vector2.Transform(ref leftTop, ref transform, out leftTop);
   7: Vector2.Transform(ref rightTop, ref transform, out rightTop);
   8: Vector2.Transform(ref leftBottom, ref transform, out leftBottom);
   9: Vector2.Transform(ref rightBottom, ref transform, out rightBottom);
  10:  
  11: Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop), Vector2.Min(leftBottom, rightBottom));
  12: Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop), Vector2.Max(leftBottom, rightBottom));
  13:  
  14: collideRectangle = new Rectangle((int)min.X, (int)min.Y, (int)(max.X - min.X), (int)(max.Y - min.Y));
  15:  

 

pic2

如上圖、碰撞矩型由左邊紅色框框轉換成右邊紅色框框。
當兩個圖形的碰撞矩型重疊的時候,真正的像素檢查就開始了!

pic3

只要檢查重疊的矩型對應的原始圖形的顏色就可以知道有沒有碰撞,但是這裡必須多一道轉換的手續!
也就是我們之前求出來的transfrom矩陣!
讓我們仔細想想,重疊矩型內部的某一點座標,乘上transform的反矩陣,就會變成原始圖型的座標,
因此我們把重疊矩型內的每一點,分別乘上A和B的transform反矩陣取出顏色,在互相比較。

   1: Vector2 A_point = Vector2.Transform(new Vector2(intersect.X, intersect.Y), Matrix.Invert(A.transform));
   2: Vector2 B_point = Vector2.Transform(new Vector2(intersect.X, intersect.Y), Matrix.Invert(B.transform));

 

但是若每個點都這樣乘,效能會很慢,所以我們改變方法,先求出重疊矩型的左上角的點,
再算出重疊矩型在x軸往右邊走一點以及在y軸往下走一點,在原始圖形上分別應該走多少,
如此我們不用每個點都乘上轉換矩陣,而是用加的即可,這省下非常多的工!

   1: Rectangle intersect = Rectangle.Intersect(A.CollideRectangle, B.CollideRectangle);
   2:  
   3:     Matrix InvertA = Matrix.Invert(A.Transform);
   4:     Matrix InvertB = Matrix.Invert(B.Transform);
   5:  
   6:     Vector2 AstepX = Vector2.TransformNormal(Vector2.UnitX, InvertA);
   7:     Vector2 AstepY = Vector2.TransformNormal(Vector2.UnitY, InvertA);
   8:     Vector2 BstepX = Vector2.TransformNormal(Vector2.UnitX, InvertB);
   9:     Vector2 BstepY = Vector2.TransformNormal(Vector2.UnitY, InvertB);
  10:  
  11:     Vector2 A_RowStart = Vector2.Transform(new Vector2(intersect.X, intersect.Y), InvertA);
  12:     Vector2 B_RowStart = Vector2.Transform(new Vector2(intersect.X, intersect.Y), InvertB);
  13:  
  14:     for (int y = 0; y < intersect.Height; y++) {
  15:  
  16:         Vector2 A = A_RowStart;
  17:         Vector2 B = B_RowStart;
  18:  
  19:         for (int x = 0; x < intersect.Width; x++) {
  20:             int Ax = (int)Math.Round(A.X);
  21:             int Ay = (int)Math.Round(A.Y);
  22:             int Bx = (int)Math.Round(B.X);
  23:             int By = (int)Math.Round(B.Y);
  24:             if (0 <= Ax && Ax < A.Image.SourceRectangle.Value.Width &&
  25:                 0 <= Ay && Ay < A.Image.SourceRectangle.Value.Height &&
  26:                 0 <= Bx && Bx < B.Image.SourceRectangle.Value.Width &&
  27:                 0 <= By && By < B.Image.SourceRectangle.Value.Height) {
  28:  
  29:                 Color colorA = pixelColors[Ax + A.Image.SourceRectangle.Value.X + (Ay + A.Image.SourceRectangle.Value.Y) * A.Image.Width];
  30:                 Color colorB = obj.PixelColors[Bx + B.Image.SourceRectangle.Value.X + (By + B.Image.SourceRectangle.Value.Y) * B.Image.Width];
  31:                 if (colorA.A != 0 && colorB.A != 0) {
  32:                     return true;
  33:                 }
  34:             }
  35:             A += AstepX;
  36:             B += BstepX;
  37:         }
  38:         A_RowStart += AstepY;
  39:         B_RowStart += BstepY;
  40:     }
  41:     return false;

此程式碼只是示意而已,3、4行求出反矩陣,6到9行求出單位向量,11、12行求出重疊矩陣的左上角在原始圖形的座標位置,
之後的for迴圈根據重疊矩型開始掃點,這裡就不再是每個點都乘上反矩陣了,我們有求出單位向量,
只要重疊矩陣往右移動一個點,就加上AstepX或BstepX,一行掃完要換下一行時,就加上AstepY和BstepY。

pic5

當我們掃到的點在兩張圖片裡都有顏色時,就表示碰撞了!