首页 > 编程 > JavaScript > 正文

JS/HTML5游戏常用算法之碰撞检测 像素检测算法实例详解

2019-11-19 12:21:05
字体:
来源:转载
供稿:网友

本文实例讲述了JS/HTML5游戏常用算法之碰撞检测 像素检测算法。分享给大家供大家参考,具体如下:

使用像素碰撞检测法算是最精确的算法了,当然,带来的代价也是比较明显的,那就是效率上的低下。除非是在极为特殊的情况下,要求使用非常精确的碰撞,否则,一般情况下在游戏中是不建议使用这种算法,特别是在运行效率不太高的HTML5游戏中。

一般来说在使用像素碰撞检测之前会使用AABB矩形包围盒先检测两个精灵是否有碰撞,如果AABB包围盒检测没有碰撞,那一定是没有碰撞到,反之,则不一定,需要进一步进行像素检测。如下图所示,很明显,虽然两个精灵的包围盒发生了碰撞,但两个精灵本身没有发生碰撞,所以在这种精灵的形状极为不规则的情况下,除非使用多边形包围盒,并且需要多边形和精灵的形状极为接近,才能够获取好的效果,而且,别忘了,多边形只适合凸多边形的情况。

这样,我们就只能采用像素检测算法达到精确检测的目的。接下来,先来看看像素碰撞的原理,首先,我们知道所有的精灵都是由像素点组成,而在canvas的像素数据中每个点都是由RGBA四个数据组成。如果某个点的A(alpha值,透明度)为0则表示该点是透明的,比如在图6-19中两个精灵的空白部分的点的A值为0。如果两个精灵发生碰撞,则表示两个精灵有像素点发生了重叠,即两个精灵的像素点坐标相同,如下图所示。

根据这个原理,基本上我们可以采取以下步骤来进行检测。

(1)选择需要检测的两个精灵。

(2)先检测两个精灵是否发生包围盒碰撞,如果没有则退出,否则获取两个矩形的相交区域,并继续。

(3)把一个精灵绘制到和游戏屏幕等大的空白的后台缓冲区中,获取缓冲区中在相交区域的像素数据。

(4)清除后台缓冲区。

(5)对另一个精灵进行步骤3的操作。

(6)得到两个精灵在同一个相交矩形的像素数据后,循环比较每一个像素点,如果两个精灵在同一位置的透明度不都是0,则表示两个精灵有相交,退出循环,返回真。

需要注意的一点是,在第3步获取相交区域的像素数据中,需要在后台另外的一个和游戏屏幕等大的空白区域中绘制,而不能直接获取游戏画面中的相交区域,因为两个精灵在相交区域中的像素已经发生了重叠【包围盒】。

由以上的算法可以看出,在进行像素检测的时候,需要另外一个缓冲区,把需要检测的精灵绘制一次,需要清空缓冲区,最后还要使用一个for循环检测像素。如果相交区域为100×100个像素点,则最坏的情况需要循环10 000 次,由此看来,这真不是一个省时的工作。

<!DOCTYPE html><html lang="en"><head> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> <meta charset="UTF-8"> <title>盒包围碰撞算法-像素检测算法</title> <style>  #stage {   border: 1px solid lightgray;  } </style></head><body><h1>包围盒是否碰撞:<span class="hitTestBox">否</span></h1><h1>像素检测是否碰撞:<span class="hitTestPixel">否</span></h1><canvas id="stage"></canvas><img src="./images/penguin.png" alt="penguinImg" id="penguinImg" style="display:none" /><img src="./images/giraffe.png" alt="giraffeImg" id="giraffeImg" style="display:none" /></body><script> window.onload = function () {  var stage = document.querySelector('#stage'),   ctx = stage.getContext('2d');  stage.width = 600;  stage.height = 600;  //创建后台canvas  var bC = document.createElement("canvas");  bC.width = stage.width;  bC.height = stage.height;  var backBuf = bC.getContext("2d");  var penguin = document.querySelector('#penguinImg'),giraffe = document.querySelector('#giraffeImg');  var penguinImg = {   x:stage.width/2,   y:stage.height/2,   r:128,   data:null  },giraffeImg = {   x:100,   y:100,   r:128,   data:null  };  function drawImageBox(img,x,y,width,height){   ctx.beginPath();   ctx.rect(x,y,width,height);   ctx.stroke();   ctx.drawImage(img,x,y,width,height);  }  //缓存画布绘制方法  function drawImageBC(img,x,y,width,height){   backBuf.clearRect(0,0,stage.width,stage.height);   backBuf.save();   backBuf.drawImage(img,x,y,width,height);   backBuf.restore();  }  //获取两个矩形相交区域  function getInRect(x1,y1,x2,y2,x3,y3,x4,y4) {   return [Math.max(x1,x3),Math.max(y1,y3),Math.min(x2,x4),Math.min(y2,y4)];  }  document.onkeydown = function (event) {   var e = event || window.event || arguments.callee.caller.arguments[0];   //根据地图数组碰撞将测   switch (e.keyCode) {    case 37:     console.log("Left");     if (penguinImg.x > 0) {      penguinImg.x -= 2;     }     break;    case 38:     console.log("Top");     if (penguinImg.y > 0) {      penguinImg.y -= 2;     }     break;    case 39:     console.log("Right");     if (penguinImg.x < stage.width) {      penguinImg.x += 2;     }     break;    case 40:     console.log("Bottom");     if (penguinImg.y < stage.height) {      penguinImg.y += 2;     }     break;    default:     return false;   }  };  stage.addEventListener('click', function (event) {   var x = event.clientX - stage.getBoundingClientRect().left;   var y = event.clientY - stage.getBoundingClientRect().top;   penguinImg.x = x;   penguinImg.y = y;  });  function update() {   ctx.clearRect(0, 0, 600, 600);   drawImageBox(giraffe,giraffeImg.x,giraffeImg.y,giraffeImg.r,giraffeImg.r);   drawImageBox(penguin,penguinImg.x,penguinImg.y,penguinImg.r,penguinImg.r);   document.querySelector('.hitTestBox').innerHTML = "否";   document.querySelector('.hitTestPixel').innerHTML = "否";   var rect = getInRect(giraffeImg.x,giraffeImg.y,giraffeImg.x+giraffeImg.r,giraffeImg.y+giraffeImg.r,penguinImg.x,penguinImg.y,penguinImg.x+penguinImg.r,penguinImg.y+penguinImg.r)   //如果没有相交则退出   if(rect[0]>=rect[2]||rect[1]>=rect[3]) {   } else{    document.querySelector('.hitTestBox').innerHTML = "是";    giraffeImg.data = null;    penguinImg.data = null;    //获取精灵在相交矩形像素数据    drawImageBC(giraffe,giraffeImg.x,giraffeImg.y,giraffeImg.r,giraffeImg.r);    giraffeImg.data = backBuf.getImageData(rect[0],rect[1],rect[2],rect[3]).data;    drawImageBC(penguin,penguinImg.x,penguinImg.y,penguinImg.r,penguinImg.r);    penguinImg.data = backBuf.getImageData(rect[0],rect[1],rect[2],rect[3]).data;    for(var i=3;i<giraffeImg.data.length;i+=4)    {     if(giraffeImg.data[i]>0&&penguinImg.data[i]>0){      document.querySelector('.hitTestPixel').innerHTML = "是";     }    }   }   requestAnimationFrame(update);  }  update(); };</script></html>

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具http://tools.VeVB.COm/code/HtmlJsRun测试运行上述代码,观察运行效果。

github地址:https://github.com/krapnikkk/JS-gameMathematics

更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《JavaScript数学运算用法总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript数组操作技巧总结》、《JavaScript排序算法总结》、《JavaScript遍历算法与技巧总结》、《JavaScript查找算法技巧总结》及《JavaScript错误与调试技巧总结

希望本文所述对大家JavaScript程序设计有所帮助。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表