40-检查两个物体的重叠部分
4.10.3 检查两个物体的重叠部分
如果检测到两个物体边界发生碰撞,就可以简单地遍历每个图片的ImageData集合中的每个像素,找到匹配的像素点,并检测其透明度是否为0。这种方法的缺点是,应用在大图片的碰撞检测中会非常慢,而且非常低效。此处使用的图片为48像素 × 48像素,共有2304个像素点。而这意味着,在每一帧里都需要对一次简单的碰撞检测进行大量的循环计算。不过,也可以找到两个物体边界的重叠部分,只比较这些像素点即可。
图4-20显示的是像素碰撞中的交点区域。
为了只比较相交区域的像素点,而不是比较所有的像素点,首先需要找出相交的区域。因此,需要对当前物体的位置、长和宽调用Javascript的Math.min()方法和Math.max()方法。
var xMin = Math.max( blueObject.x, redObject.x );
var yMin = Math.max( blueObject.y, redObject.y );
var xMax = Math.min( blueObject.x+blueObject.width, redObject.x+redObject.width );
var yMax = Math.min( blueObject.y+blueObject.height, redObject.y+redObject.height
通过比较两个物体的位置以及宽度(或者高度,这取决与坐标轴),可以得到用于定义相交区域的4个值。
变量xMin和yMin给出了相交区域的左上角位置,变量xMax和变量yMax给出了相交区域的右下角位置。
接下来,创建一个嵌套循环,遍历相交区域的所有像素点的x坐标和y坐标。检测两个物体相交区域的所有点,并将像素透明度的值与0进行比较。
(1)pixelX循环:
for ( var pixelX = xMin; pixelX < xMax; pixelX++ )
接着创建一个循环,遍历相交区域所有点的y坐标。
(2)嵌套的pixelY循环:
for ( var pixelY = yMin; pixelY < yMax; pixelY++ )
当用pixelX和pixelY确定一个像素点时,需要找到该像素点位于不同物体内的透明度。图片的数据信息是一个简单一维数组,数组里的每一项是像素点的4个信息。其中,第四项数据是透明度信息。因此,可以通过以下步骤获取单个像素点的透明度信息:
① 取像素点的pixelX与当前物体的x值的差值;
② 取像素点的pixelY与当前物体的y值的差值,将结果乘以对象的宽度,然后与第一步的结果相加;
③ 将得到的值乘以4(因为每个像素在数组中表示为4个值);
④ 将得到的值与3相加;
听起来很复杂,但实际的代码并不复杂。
var bluepixel = ((pixelX-blueObject.x ) + (pixelY-blueObject.y )
*blueObject.width )*4 + 3 ;
var redpixel = ((pixelX-redObject.x) + (pixelY-redObject.y)
*redObject.width)*4 + 3 ;
碰撞检测
当获取到两个像素点的透明度后,判断两个物体是否发生碰撞就非常容易了。只需要确定蓝色像素点和红色像素点的透明度是否都不为0即可。
if (( blueObject.blueImageData.data [ bluepixel ] !== 0 )
&&( redObject.redImageData.data [ redpixel ] !== 0 )) {
console.log("pixel collision")
blueObject.dx=0;
redObject.dx=0;
break;
}
可以看到,代码里添加了控制台打印信息,并将两个物体的dx设置为0,让它们停止运动。图4-21所示为运行截图。
例4-18包含了基于像素透明度的碰撞测试的所有代码。
例4-18 通过像素透明度判断物体是否发生碰撞
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Chapter 4 Example 18: Pixel Collisions</title>
<script src="modernizr.js"></script>
<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false);
function eventWindowLoaded() {
canvasApp();
}
function canvasSupport () {
return Modernizr.canvas;
}
function canvasApp(){
if (!canvasSupport()) {
return;
}else{
var theCanvas = document.getElementById('canvas');
var context = theCanvas.getContext('2d');
}
var blueObject={};
var redObject={};
blueObject.x=0;
blueObject.y=200;
blueObject.dx=2;
blueObject.width=48;
blueObject.height=48;
blueObject.image=new Image();
blueObject.image.src="blueplus.png";
context.drawImage(blueObject.image, 0, 0);
blueObject.blueImageData=context.getImageData(0, 0, blueObject.width,
blueObject.height);
context.clearRect(0,0,theCanvas.width, theCanvas.height);
redObject.x=348;
redObject.y=200;
redObject.dx=-2;
redObject.width=48;
redObject.height=48;
redObject.image=new Image();
redObject.image.src="redcircle.png";
context.drawImage(redObject.image, 0, 0);
redObject.redImageData=context.getImageData(0, 0, redObject.width,
redObject.height);
context.clearRect(0,0,theCanvas.width, theCanvas.height);
function drawScreen() {
blueObject.x+=blueObject.dx;
redObject.x+=redObject.dx;
context.clearRect(0,0,theCanvas.width, theCanvas.height);
context.drawImage(blueObject.image, blueObject.x, blueObject.y);
context.drawImage(redObject.image, redObject.x, redObject.y);
console.log("redObject.redImageData.data[3]=" +
redObject.redImageData.data[3]);
if (boundingBoxCollide(blueObject, redObject)){
console.log("bounding box collide");
var xMin = Math.max( blueObject.x, redObject.x );
var yMin = Math.max( blueObject.y, redObject.y );
var xMax = Math.min( blueObject.x+blueObject.width,
redObject.x+redObject.width );
var yMax = Math.min( blueObject.y+blueObject.height,
redObject.y+redObject.height );
for ( var pixelX = xMin; pixelX < xMax; pixelX++ ) {
for ( var pixelY = yMin; pixelY < yMax; pixelY++ ) {
var bluepixel = ((pixelX-blueObject.x ) + (pixelY-blueObject.y )
*blueObject.width )*4 + 3 ;
var redpixel = ((pixelX-redObject.x) +
(pixelY-redObject.y)*redObject.width)*4 + 3 ;
if (( blueObject.blueImageData.data [ bluepixel ] !== 0 ) &&
( redObject.redImageData.data[ redpixel ] !== 0 )) {
console.log("pixel collision")
blueObject.dx=0;
redObject.dx=0;
break;
}
}
}
}
}
function boundingBoxCollide(object1, object2) {
var left1 = object1.x;
var left2 = object2.x;
var right1 = object1.x + object1.width;
var right2 = object2.x + object2.width;
var top1 = object1.y;
var top2 = object2.y;
var bottom1 = object1.y + object1.height;
var bottom2 = object2.y + object2.height;
if (bottom1 < top2) return(false);
if (top1 > bottom2) return(false);
if (right1 < left2) return(false);
if (left1 > right2) return(false);
return(true);
};
function startUp(){
gameLoop();
}
function gameLoop() {
window.setTimeout(gameLoop, 100);
drawScreen();
}
startUp();
}
</script>
</head>
<body>
<div>
<canvas id="canvas" width="400" height="400" style="position: absolute; top:
50px; left: 50px;">
Your browser does not support the HTML 5 Canvas.
</canvas>
</div>
</body>
</html>