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


Posted in Javascript onDecember 12, 2018

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

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

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

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

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

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

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

(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.3water.com/code/HtmlJsRun测试运行上述代码,观察运行效果。

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

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

Javascript 相关文章推荐
你真的了解JavaScript吗?
Feb 24 Javascript
jquery isType() 类型判断代码
Feb 14 Javascript
JavaScript高级程序设计(第3版)学习笔记7 js函数(上)
Oct 11 Javascript
jQuery方法简洁实现隔行换色及toggleClass的使用
Mar 15 Javascript
jQuery实现点击该行即可删除HTML表格行
Oct 17 Javascript
node.js中的fs.stat方法使用说明
Dec 16 Javascript
JavaScript设计模式经典之工厂模式
Feb 24 Javascript
Boostrap模态窗口的学习小结
Mar 28 Javascript
jquery实现全选和全不选功能效果的实现代码【推荐】
May 05 Javascript
微信小程序tabBar 返回tabBar不刷新页面
Jul 25 Javascript
js实现点赞效果
Mar 16 Javascript
比较node.js和Deno
Apr 27 Javascript
d3绘制基本的柱形图的实现代码
Dec 12 #Javascript
JS/HTML5游戏常用算法之碰撞检测 地图格子算法实例详解
Dec 12 #Javascript
JS/HTML5游戏常用算法之追踪算法实例详解
Dec 12 #Javascript
js使用swiper实现层叠轮播效果实例代码
Dec 12 #Javascript
如何制作一个Node命令行图像识别工具
Dec 12 #Javascript
JS遍历JSON数组及获取JSON数组长度操作示例【测试可用】
Dec 12 #Javascript
ionic使用angularjs表单验证(模板验证)
Dec 12 #Javascript
You might like
BBS(php &amp; mysql)完整版(八)
2006/10/09 PHP
php不用正则采集速度探究总结
2008/03/24 PHP
PHP基础陷阱题(变量赋值)
2012/09/12 PHP
PHP屏蔽过滤指定关键字的方法
2014/11/03 PHP
php替换字符串中间字符为省略号的方法
2015/05/04 PHP
PHP实现微信网页授权开发教程
2016/01/19 PHP
PHP安装memcache扩展的步骤讲解
2019/02/14 PHP
PHP切割汉字的常用方法实例总结
2019/04/27 PHP
Smarty缓存机制实例详解【三种缓存方式】
2019/07/20 PHP
比较全面的event对像在IE与FF中的区别 推荐
2009/09/21 Javascript
不一样的文字闪烁 轮番闪烁
2009/11/11 Javascript
jquery提取元素里的纯文本不包含span等里的内容
2013/09/30 Javascript
NodeJS学习笔记之FS文件模块
2015/01/13 NodeJs
js获取数组的最后一个元素
2015/04/14 Javascript
详解Matlab中 sort 函数用法
2016/03/20 Javascript
基于JS实现bookstore静态页面的实例代码
2017/02/22 Javascript
vue组件watch属性实例讲解
2017/11/07 Javascript
mock.js模拟前后台交互
2019/07/25 Javascript
Python删除空文件和空文件夹的方法
2015/07/14 Python
python配置文件写入过程详解
2019/10/19 Python
pytorch逐元素比较tensor大小实例
2020/01/03 Python
Python模拟登录requests.Session应用详解
2020/11/17 Python
python3中布局背景颜色代码分析
2020/12/01 Python
京东国际站:JOYBUY
2017/11/23 全球购物
爱尔兰最大的体育零售商:Life Style Sports
2019/06/12 全球购物
数据库基础的一些面试题
2012/02/25 面试题
什么是方法的重载
2013/06/24 面试题
儿科护理实习自我鉴定
2013/09/19 职场文书
教师师德教育的自我评价
2013/10/31 职场文书
投标邀请书范文
2014/01/31 职场文书
竞赛口号大全
2014/06/16 职场文书
2014年依法行政工作总结
2014/11/19 职场文书
高校自主招生自荐信2015
2015/03/04 职场文书
商务英语求职信范文
2015/03/19 职场文书
保留意见审计报告
2015/06/05 职场文书
2016优秀护士先进个人事迹材料
2016/02/25 职场文书