当前位置:嗨网首页>书籍在线阅读

40-检查两个物体的重叠部分

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

4.10.3 检查两个物体的重叠部分

如果检测到两个物体边界发生碰撞,就可以简单地遍历每个图片的ImageData集合中的每个像素,找到匹配的像素点,并检测其透明度是否为0。这种方法的缺点是,应用在大图片的碰撞检测中会非常慢,而且非常低效。此处使用的图片为48像素 × 48像素,共有2304个像素点。而这意味着,在每一帧里都需要对一次简单的碰撞检测进行大量的循环计算。不过,也可以找到两个物体边界的重叠部分,只比较这些像素点即可。

图4-20显示的是像素碰撞中的交点区域。

76.png

图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所示为运行截图。

77.png

图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>