[转载]BitmapHitTest

本文介绍了一种复杂的像素级精确碰撞检测算法,包括基本的矩形碰撞检测和更精细的像素级碰撞检测实现。该算法通过多次缩小检测区域来提高效率,适用于Flash平台上的游戏开发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


package

{



import flash.display.BitmapData;

import flash.display.BlendMode;

import flash.display.DisplayObject;

import flash.display.Sprite;



import flash.geom.ColorTransform;

import flash.geom.Matrix;

import flash.geom.Point;

import flash.geom.Rectangle;



public class BitmapHitTest

{


public static function complexHitTestObject( target1:DisplayObject, target2:DisplayObject, accurracy:Number = 1 ):Boolean

{

return complexIntersectionRectangle( target1, target2, accurracy ).width != 0;

}



public static function intersectionRectangle( target1:DisplayObject, target2:DisplayObject ):Rectangle

{

// If either of the items don't have a reference to stage, then they are not in a display list

// or if a simple hitTestObject is false, they cannot be intersecting.

if( !target1.root || !target2.root || !target1.hitTestObject( target2 ) ) return new Rectangle();



// Get the bounds of each DisplayObject.

var bounds1:Rectangle = target1.getBounds( target1.root );

var bounds2:Rectangle = target2.getBounds( target2.root );



// Determine test area boundaries.

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;

}



public static function complexIntersectionRectangle( target1:DisplayObject, target2:DisplayObject, accurracy:Number = 1 ):Rectangle

{


if( accurracy <= 0 ) throw new Error( "ArgumentError: Error #5001: Invalid value for accurracy", 5001 );



// If a simple hitTestObject is false, they cannot be intersecting.

if( !target1.hitTestObject( target2 ) ) return new Rectangle();



var hitRectangle:Rectangle = intersectionRectangle( target1, target2 );

// If their boundaries are no interesecting, they cannot be intersecting.

if( hitRectangle.width * accurracy <1 || hitRectangle.height * accurracy <1 ) return new Rectangle();



var bitmapData:BitmapData = new BitmapData( hitRectangle.width * accurracy, hitRectangle.height * accurracy, false, 0x000000 );


// Draw the first target.

bitmapData.draw( target1, BitmapHitTest.getDrawMatrix( target1, hitRectangle, accurracy ), new ColorTransform( 1, 1, 1, 1, 255, -255, -255, 255 ) );
//trace(BitmapHitTest.getDrawMatrix( target1, hitRectangle, accurracy ))
//trace(BitmapHitTest.getDrawMatrix( target2, hitRectangle, accurracy ))
// Overlay the second target.

bitmapData.draw( target2, BitmapHitTest.getDrawMatrix( target2, hitRectangle, accurracy ), new ColorTransform( 1, 1, 1, 1, 255, 255, 255, 255 ), BlendMode.DIFFERENCE );



// Find the intersection.

var intersection:Rectangle = bitmapData.getColorBoundsRect( 0xFFFFFFFF,0xFF00FFFF );



bitmapData.dispose();



// Alter width and positions to compensate for accurracy

if( accurracy != 1 )

{

intersection.x /= accurracy;

intersection.y /= accurracy;

intersection.width /= accurracy;

intersection.height /= accurracy;

}



intersection.x += hitRectangle.x;

intersection.y += hitRectangle.y;



return intersection;

}





protected static function getDrawMatrix( target:DisplayObject, hitRectangle:Rectangle, accurracy: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;

matrix.ty = localToGlobal.y - hitRectangle.y;



matrix.a = matrix.a / rootConcatenatedMatrix.a;

matrix.d = matrix.d / rootConcatenatedMatrix.d;

if( accurracy != 1 ) matrix.scale( accurracy, accurracy );


return matrix;

}


}


}






package

{
//像素级精确碰撞算法优化
//代码:Aone
//2009-10-30
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 BitmapHitTestPlus

{

public static var tileSize:int=20
public static function complexHitTestObject(target1:DisplayObject, target2:DisplayObject):Boolean
{
//横向缩小到tileSize尺寸需要的倍数,也就是期望检测的时候缩小到的尺寸。
var scaleX:Number = (target1.width < target2.width ? target1.width : target2.width) / BitmapHitTestPlus.tileSize
//纵向缩小到tileSize尺寸需要的倍
var scaleY:Number = (target1.height < target2.height ? target1.height : target2.height) / BitmapHitTestPlus.tileSize
//
//如果倍数小于1则按原始倍率也就是原始尺寸
scaleX = scaleX < 1 ? 1 : scaleX
scaleY = scaleY < 1 ? 1 : scaleY
//draw用point
var pt:Point=new Point()
//做2次draw时使用的颜色
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,BitmapHitTestPlus.tileSize,BitmapHitTestPlus.tileSize).width != 0;

}

public static function intersectionRectangle(target1:DisplayObject, target2:DisplayObject):Rectangle

{

// If either of the items don't have a reference to stage, then they are not in a display list

// or if a simple hitTestObject is false, they cannot be intersecting.

if (!target1.root || !target2.root || !target1.hitTestObject(target2))
return new Rectangle();


// Get the bounds of each DisplayObject.

var bounds1:Rectangle=target1.getBounds(target1.root);

var bounds2:Rectangle=target2.getBounds(target2.root);


// Determine test area boundaries.

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;

}

public static 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
//建立一个用来draw的临时BitmapData对象,尺寸为期望宽nowW,期望高nowH
var bitmapData:BitmapData=new BitmapData(nowW,nowH, true, 0);
//绘制对象1其缩放比例和位移量由getDrawMatrix()函数计算,并且把不透明处绘制为ct的颜色
bitmapData.draw(target1, BitmapHitTestPlus.getDrawMatrix(target1, hitRectangle , scaleX , scaleY ),ct);
//当纵横缩小比例不为1的时候把任何有色像素重新替换成ct的颜色0xFF00000F,原因为缩小的对象在进行纯色绘制的时候会有半透明像素产生,如果不加以重新替换会影响后面对象2的滤镜效果。
if(scaleX!=1&&scaleY!=1){
bitmapData.threshold(bitmapData,bitmapData.rect,pt,">",0,0xFF00000F)
}
//绘制对象2其缩放比例和位移量由getDrawMatrix()函数计算,并且把不透明处绘制为ct的颜色,并且模式为叠加。如此一来两个对象重叠的部分颜色值必定大于0xFF00000F。
bitmapData.draw(target2, BitmapHitTestPlus.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=BitmapHitTestPlus.tileSize
var nextH:int=BitmapHitTestPlus.tileSize
//如果此次判定发现碰撞区域和bitmapData尺寸相同,那么在计算下次需要判断区域时候会和此次的区域相同,那么判断结果可能会和此次结果相同。这样则会引起堆栈上溢的情况,为了避免该情况发生,将缩小判断的尺寸扩大一倍进行再次检测。
if(intersection.width!=nowW){
nextW=BitmapHitTestPlus.tileSize
}else{
nextW=nowW*2
}
if(intersection.height!=nowH){
nextH=BitmapHitTestPlus.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;

}

protected static 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;

}


}


}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值