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軸旋轉、再移動到圖形的螢幕座標。
此矩陣就意味著原始圖形的每個像素座標乘上此矩陣,就會變成顯示在螢幕上的座標。
接著,因為圖形有旋轉,所以碰撞矩型也要跟著改變,
先求出原始圖形的四個頂點座標,在分別乘上轉換矩陣,得出顯示在螢幕上實際的四個頂點座標
取最大的值,當做新的碰撞矩型,程式碼如下:
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:
如上圖、碰撞矩型由左邊紅色框框轉換成右邊紅色框框。
當兩個圖形的碰撞矩型重疊的時候,真正的像素檢查就開始了!
只要檢查重疊的矩型對應的原始圖形的顏色就可以知道有沒有碰撞,但是這裡必須多一道轉換的手續!
也就是我們之前求出來的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。
當我們掃到的點在兩張圖片裡都有顏色時,就表示碰撞了!