JS/HTML5游戏常用算法之碰撞检测 像素检测算法实例详解
本文实例讲述了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.jb51.net/code/htmljsrun测试运行上述代码,观察运行效果。
github地址:https://github.com/krapnikkk/js-gamemathematics
更多关于javascript相关内容感兴趣的读者可查看本站专题:《javascript数学运算用法总结》、《javascript数据结构与算法技巧总结》、《javascript数组操作技巧总结》、《javascript排序算法总结》、《javascript遍历算法与技巧总结》、《javascript查找算法技巧总结》及《javascript错误与调试技巧总结》
希望本文所述对大家javascript程序设计有所帮助。
推荐阅读
-
JS/HTML5游戏常用算法之路径搜索算法 随机迷宫算法详解【普里姆算法】
-
JS/HTML5游戏常用算法之路径搜索算法 A*寻路算法完整实例
-
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【凹多边形的分离轴检测算法】
-
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【矩形情况】
-
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【圆形情况】
-
JS/HTML5游戏常用算法之碰撞检测 地图格子算法实例详解
-
JS/HTML5游戏常用算法之追踪算法实例详解
-
JS/HTML5游戏常用算法之碰撞检测 像素检测算法实例详解
-
JS/HTML5游戏常用算法之碰撞检测 像素检测算法实例详解
-
JS/HTML5游戏常用算法之碰撞检测 地图格子算法实例详解