关于tink的碰撞检测类【2】

分析算法的思路:

 

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的那个类应该很简单了。并且,个人认为他的类存在一些错误,下篇讨论
原文地址:https://www.cnblogs.com/weiweishuo/p/3082646.html