分析演算法的思路:
Step1:假設stage(黑色)上有4個顯示對象red_mc,green_mc,blue_mc,yellow_mc,層級關係是stage>root>red_mc,stage>root>yellow_mc>blue_mc>green_mc。要檢測碰撞的對象是red_mc和green_mc。
圖1
補充約定:為方便表述,這裡約定顯示對象的“初始狀態”為相對父級容器座標系未進行旋轉,縮放,即對象的transform.matrix對象是[a=1,b=0,c=0,d=1]。
Step2:一開始,圖1中的4個mc都處於初始狀態。但這樣的碰撞檢測不考驗演算法,因此對它們做縮放,旋轉,平移處理(我直接在flashIDE裡完成的),使得red_mc和green_mc的空間位置足夠複雜。
圖2
補充知識:draw()方法的一個細節
如下代碼(文檔類):
varbmd1:BitmapData = new BitmapData(150,150);
bmd1.draw(red_mc);
varbmp1:Bitmap = new Bitmap(bmd1);
this.addChild(bmp1);
若red_mc處於初始狀態(即圖1中的red_mc),效果如下:
圖3
十分正常。但是,若用同樣地代碼來draw變形後的red_mc(即圖2中的red_mc),會是什麼樣子呢?圖2中red_mc尺寸太大,這塊兒150*150的白色像素會不會只能draw入red_mc的一部分呢?效果如下:
圖4
看來,仍舊是以初始狀態的red_mc為繪製源。
這就是draw(X)方法的一個細節:不管X怎樣變形,平移,都選取X的初始狀態作為繪製源。此外,當X處於層層容器嵌套之下時(green_mc就是這種情況),不管它n多父級的變形操作對X形狀產生怎樣影響,draw也一樣選取X的初始狀態為繪製源。
Step3:前兩步作為準備,現在開始碰撞檢測的第一步,利用flash內建的hitTestObject()函數做預判斷。若這一步判斷false肯定沒戲了。
補充知識:
1.hitTestObject()是基於AABB包圍體的碰撞檢測,即檢測虛線框的碰撞。(red_mc的虛線框看似並未僅包住red_mc,這是因為red_mc
圖5
進行過旋轉變換。)
2.hitTestObject()檢測碰撞時,並不關心對象是否在顯示列表內。這點很重要。
Step4.通過上步的預判斷,再進一步檢測:擷取兩個包圍體的交疊矩形地區,用rect1儲存相關資訊(x,y,width,height),判斷rect1地區內red_mc和green_mc是否有像素重疊。若true便碰上了。
補充知識:顯示對象的matrix屬性
matrix本身的a,b,c,d沒什麼好說,這裡記下matrix操作的細節。
1.red_mc.transform.matrix,這個記錄矩陣(習慣稱它記錄矩陣,文檔上稱“變換矩陣”),是red_mc相對於父級容器座標系的記錄矩陣。就像通常所說red_mc.x也是red_mc相對於父級座標系的x軸位移。
2.要想獲得red_mc相對於再上一級,即parent.parent的記錄矩陣,該如何呢?直接用parent的matrix乘上red_mc的matrix就好了。
3.若想擷取red_mc相對於stage的記錄矩陣呢?理論上是root.transform.matrix*........(各級父容器的矩陣逐級相乘)......*red_mc.transform.matrix。很累,flash為此提供了concatenatedMatrix屬性,就是顯示對象相對於stage的記錄矩陣。(矩陣乘法是不能用*號的,應該是concat,我是想寫快點兒)
4.假若要擷取red_mc相對於root的記錄矩陣呢?直接逐級相乘沒問題。不過,concatenated屬性提供了一個簡潔思路------varrootM:Matrix=root.transform.matrix.clone();
var rootM1:Matrix=red_mc.transform.concatenatedMatrix;
rootM.invert();//取得root的逆矩陣
rootM1.concatenatedMatrix.concat(rootM);//這樣,rootM1就轉化為相對於root的
5.matrix的concat()方法似有bug
當a,d任意為0時,計算的結果就不大對,例如下面兩個矩陣
var A:Matrix=newMatrix(1,1,2,2,0,0);
var B:Matrix=newMatrix(0,0,1,1,1,0);
A.concat(B);
trace(A);//輸出的ty不是2,我用筆算了幾遍,ty都是2
當a,d都不為0時,計算結果沒有問題,以後還是放心用吧,因為a,d任意為0,顯示對象便不存在了,應不會存在這種情況。
自己寫了個矩陣運算類,是很無腦的演算法:行對列相乘。很慢很穩妥。是不是內建的矩陣乘法運算採用另外演算法,才會有這樣的bug呢?
6.matrix的concat()方法用起來要小心順序
A.concat(B),對應的數學式為(矩陣B*矩陣A),矩陣乘法不滿足交換律,這個地方跟常規思路又不同(至少跟我想的順序相反),因此操作時應小心。
7.不要直接修改顯示對象matrix的a,b,c,d,tx,ty屬性,也不要直接應用scale,rotate等操作。因為任何設定都是無效的。
如:varA:Matrix=red_mc.taransform.matrix;//A引用的是red_mc.taransform.matrix的一份拷貝
A.scale(0.5,0.5);//編譯不會報錯,但這個操作只修改A,對red_mc.taransform.matrix不造成修改
A.a=2;//同上
正確的操作是這樣的:
varA:Matrix=red_mc.transform.matrix.clone();
//varA:Matrix=red_mc.transform.matrix也行
A.scale(0.5,0.5);
A.a=2;
red_mc.tansform.matrix=A;//建立一個合適矩陣,交付給red_mc.transform.matrix引用。
8.想對matrix執行scale操作時,用scale方法:
A.sacle(0.5,0.5);
不要寫:A.a/=A.b/=2;當matrix的b,c屬性不為0,這個操作完成的並不是scale功能。
補充知識:BlendMode.DIFFERENCE
文檔講的很清楚:
將顯示對象的原色與背景顏色進行比較,然後從較亮的原色值中減去較暗的原色值。此設定通常用於得到更明亮的顏色。
例如,如果顯示對象的某個像素的 RGB 值為0xFFCC33,背景像素的 RGB 值為 0xDDF800,則顯示像素的結果 RGB 值為 0x222C33(因為 0xFF -0xDD = 0x22,0xF8 - 0xCC = 0x2C,且 0x33 - 0x00 = 0x33)。
補充知識:draw()方法的matrix參數有何效果
文檔說:用於縮放、旋轉位元影像或轉換位元影像的座標
下面用代碼和說明:
varbmd1:BitmapData = new BitmapData(200,200);
bmd1.draw(red_mc,newMatrix(1,0,0,1,0,0));//先不對繪製源做處理,看看正常的樣子
varbmp1:Bitmap = new Bitmap(bmd1);
this.addChild(bmp1);
圖6
很正常,跟圖4吻合
再看:
varbmd1:BitmapData = new BitmapData(200,200);
bmd1.draw(red_mc,newMatrix(1,0,0,1,110,110));//讓繪製源沿x,y軸正方向平移了(110,110)的向量
varbmp1:Bitmap = new Bitmap(bmd1);
this.addChild(bmp1);
圖7
果然,繪製源相對註冊點位移了(110,110)
再看:
varbmd1:BitmapData = new BitmapData(200,200);
bmd1.draw(red_mc,new Matrix(2, 0, 0, 2, 0, 0));
varbmp1:Bitmap = new Bitmap(bmd1);
this.addChild(bmp1);
圖8
果然縮放了兩倍
這便是matrix參數的功能。
問:如何才能draw到圖8中舞台下部的red_mc的形貌呢?
答:肉眼看到的red_mc的形貌,是它在stage座標系中的形貌,因此將draw參數設定為red_mc.transform.concatenatedMatrix,這樣繪製源便從初始狀態變換到相對舞台的形貌。
試一下:
varbmd1:BitmapData = new BitmapData(200, 200);
//為了便於區分,施加一個colorTransform讓draw到的像素變為藍色
bmd1.draw(red_mc,a_mc.transform.concatenatedMatrix,newColorTransform(1,1,1,-255,-255,255));
varbmp1:Bitmap = new Bitmap(bmd1);
this.addChild(bmp1);
圖9
什麼也沒有?因為像素塊太小了,red_mc經過矩陣變換,已經跑到了它外面去。
將像素塊增大,看看:
varbmd1:BitmapData = new BitmapData(300, 300);
//為了便於區分,施加一個colorTransform讓draw到的像素變為藍色
bmd1.draw(a_mc,a_mc.transform.concatenatedMatrix,newColorTransform(1,1,1,1,-255,-255,255,255));
varbmp1:Bitmap = new Bitmap(bmd1);
this.addChild(bmp1);
圖10
這下好啦!很准。
但,假如像素塊兒尺寸因為某種原因不能修改,該怎麼辦?那就再折磨draw()方法的matrix參數,讓red_mc平移到舞台原點:
varbmd1:BitmapData = new BitmapData(200, 200);
//為了便於區分,施加一個colorTransform讓draw到的像素變為藍色
var m1:Matrix= a_mc.transform.concatenatedMatrix;
m1.tx += 0 -m1.tx;
m1.ty += 0 -m1.ty;
bmd1.draw(a_mc,m1,newColorTransform(1,1,1,1,-255,-255,255,255));
varbmp1:Bitmap = new Bitmap(bmd1);
this.addChild(bmp1);
效果:
圖11
就是要這個樣子。
剛才說到Step4:擷取兩個包圍體的交疊矩形地區,用rect1儲存相關資訊(x,y,width,height),判斷rect1地區內red_mc和green_mc是否有像素重疊。
圖12
判斷rect1地區內red_mc和green_mc是否有像素重疊的思路:
new出來一塊兒跟rect1等大的黑色像素塊。用一種colorTransform去drawrect1地區內的red_mc,再用另一種colorTransform去draw rect1地區內的green_mc,同時採用blendMode.DIFFERENCE混合模式,一旦出現特定顏色,則有像素重疊。
至於如何準確的draw到rect1地區內的red_mc/green_mc的像素,在補充裡已經給出思路。
現在再看tink的那個類應該很簡單了。並且,個人認為他的類存在一些錯誤,下篇討論