1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
| package
{ import flash.display.BitmapData; import flash.display.BlendMode; import flash.display.DisplayObject; import flash.geom.ColorTransform; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle;
public class PixHitTestH
{
private var tileSize:int = 20
public function PixHitTestH():void {
}
public function checkObject(target1:DisplayObject, target2:DisplayObject):Boolean { var scaleX:Number = (target1.width < target2.width ? target1.width : target2.width) / tileSize var scaleY:Number = (target1.height < target2.height ? target1.height : target2.height) / tileSize scaleX = scaleX < 1 ? 1 : scaleX scaleY = scaleY < 1 ? 1 : scaleY var pt:Point = new Point() var ct:ColorTransform = new ColorTransform() ct.color = 0xFF00000F var oldHitRectangle:Rectangle = intersectionRectangle(target1, target2) var hitRectangle:Rectangle = new Rectangle() return complexIntersectionRectangle(target1, target2, scaleX, scaleY, pt, ct, oldHitRectangle, hitRectangle, tileSize, tileSize).width != 0; }
private function intersectionRectangle(target1:DisplayObject, target2:DisplayObject):Rectangle
{
if (!target1.root || !target2.root || !target1.hitTestObject(target2)) return new Rectangle();
var bounds1:Rectangle = target1.getBounds(target1.root);
var bounds2:Rectangle = target2.getBounds(target2.root);
var intersection:Rectangle = new Rectangle();
intersection.x = Math.max(bounds1.x, bounds2.x);
intersection.y = Math.max(bounds1.y, bounds2.y);
intersection.width = Math.min((bounds1.x + bounds1.width) - intersection.x, (bounds2.x + bounds2.width) - intersection.x);
intersection.height = Math.min((bounds1.y + bounds1.height) - intersection.y, (bounds2.y + bounds2.height) - intersection.y);
return intersection;
}
private function complexIntersectionRectangle(target1:DisplayObject, target2:DisplayObject, scaleX:Number, scaleY:Number, pt:Point, ct:ColorTransform, oldhitRectangle:Rectangle, hitRectangle:Rectangle, nowW:int, nowH:int):Rectangle
{ if (!target1.hitTestObject(target2)) return new Rectangle(); hitRectangle.x = oldhitRectangle.x hitRectangle.y = oldhitRectangle.y hitRectangle.width = oldhitRectangle.width / scaleX hitRectangle.height = oldhitRectangle.height / scaleY var bitmapData:BitmapData = new BitmapData(nowW, nowH, true, 0); bitmapData.draw(target1, getDrawMatrix(target1, hitRectangle, scaleX, scaleY), ct); if (scaleX != 1 && scaleY != 1) { bitmapData.threshold(bitmapData, bitmapData.rect, pt, \">\", 0, 0xFF00000F) } //绘制对象2其缩放比例和位移量由getDrawMatrix()函数计算,并且把不透明处绘制为ct的颜色,并且模式为叠加。如此一来两个对象重叠的部分颜色值必定大于0xFF00000F。 bitmapData.draw(target2, getDrawMatrix(target2, hitRectangle, scaleX, scaleY), ct, BlendMode.ADD); //把所有颜色值大于0xFF00000F的部分(也就是重叠部分)重新替换为不透明红色方便后面getColorBoundsRect()方法计算尺寸。这里替换的原因是getColorBoundsRect()不支持范围取色而只支持单色计算。 //对象1缩放后可以重新替换掉透明色,但是对象2则无法使用同一方法,但是对象2由于也是经过缩放绘制也会有半透明像素,那么重叠部分虽然全部大于0xFF00000F,但未必是统一的。 var hits:int = bitmapData.threshold(bitmapData, bitmapData.rect, pt, \">\", 0xFF00000F, 0xFFFF0000) //判断红色区域尺寸 var intersection:Rectangle = bitmapData.getColorBoundsRect(0xFFFFFFFF, 0xFFFF0000); //
bitmapData = null //如果红色区域宽度不为0,即bitmapData中含有红色像素。此时说明对象1和对象2在此次判定中有重叠有碰撞 if (intersection.width != 0) { //如果纵横缩放比例有任意一个不是原始尺寸 if (scaleX > 1 || scaleY > 1) { //并且红色像素的数量比较少,对象1和对象2的碰撞面积比较小的话 if (hits <= (scaleX + scaleY) * 1.5) { //由于bitmapData的宽高坐标都是以整数表示,那么经过缩放后取整的区域势必回又可能在取整的时候把真正可能产生碰撞的区域忽略。 //所以要进行下一次检测时候适当的把检测区域扩大xadd和yadd就是这个扩大的系数 var xadd:int = .5 var yadd:int = .5 //下次检测时候bitmapData的期望大小 var nextW:int = tileSize var nextH:int = tileSize //如果此次判定发现碰撞区域和bitmapData尺寸相同,那么在计算下次需要判断区域时候会和此次的区域相同,那么判断结果可能会和此次结果相同。这样则会引起堆栈上溢的情况,为了避免该情况发生,将缩小判断的尺寸扩大一倍进行再次检测。 if (intersection.width != nowW) { nextW = tileSize } else { nextW = nowW * 2 } if (intersection.height != nowH) { nextH = tileSize } else { nextH = nowH * 2 } //根据检测出来的缩的碰撞区域来计算未缩小的碰撞区域大小以方便下一次计算的时候缩小检测范围。 oldhitRectangle.x += (intersection.x - xadd) * scaleX oldhitRectangle.y += (intersection.y - yadd) * scaleY oldhitRectangle.width = (intersection.width + xadd * 2) * scaleX oldhitRectangle.height = (intersection.height + yadd * 2) * scaleY //根据检测期望缩小到的尺寸重新计算缩小倍率 scaleX = (oldhitRectangle.width / nextW) scaleY = (oldhitRectangle.height / nextH) //如果倍率小于2则直接按原始尺寸 scaleX = scaleX < 2 ? 1 : scaleX scaleY = scaleY < 2 ? 1 : scaleY //进行下一次判定 intersection = complexIntersectionRectangle(target1, target2, scaleX, scaleY, pt, ct, oldhitRectangle, hitRectangle, nextW, nextH) } } }
return intersection;
}
private function getDrawMatrix(target:DisplayObject, hitRectangle:Rectangle, scaleX:Number, scaleY:Number):Matrix {
var localToGlobal:Point; var matrix:Matrix; var rootConcatenatedMatrix:Matrix = target.root.transform.concatenatedMatrix;
localToGlobal = target.localToGlobal(new Point());
matrix = target.transform.concatenatedMatrix;
matrix.tx = (localToGlobal.x - hitRectangle.x) / scaleX;
matrix.ty = (localToGlobal.y - hitRectangle.y) / scaleY;
matrix.a = matrix.a / rootConcatenatedMatrix.a / scaleX; matrix.d = matrix.d / rootConcatenatedMatrix.d / scaleY;
return matrix;
}
}
}
|