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 相关文章推荐
JQuery读取XML文件数据并显示的实现代码
Dec 16 Javascript
jQuery实现 注册时选择阅读条款 左右移动
Apr 11 Javascript
将input file的选择的文件清空的两种解决方案
Oct 21 Javascript
为jquery的ajaxfileupload增加附加参数的方法
Mar 04 Javascript
JavaScript入门系列之知识点总结
Mar 24 Javascript
ECMAScript6快速入手攻略
Jul 18 Javascript
bootstrap中的 form表单属性role=&quot;form&quot;的作用详解
Jan 20 Javascript
D3.js实现简洁实用的动态仪表盘的示例
Apr 04 Javascript
微信小程序实现多选框全选与取消全选功能示例
May 14 Javascript
Vue3.0结合bootstrap创建多页面应用
May 28 Javascript
使用layer.msg 时间设置不起作用的解决方法
Sep 12 Javascript
一文带你理解vue创建一个后台管理系统流程(Vue+Element)
May 18 Vue.js
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
Win2000+Apache+MySql+PHP4+PERL安装使用小结
2006/10/09 PHP
php Mysql日期和时间函数集合
2007/11/16 PHP
php 将excel导入mysql
2009/11/09 PHP
PHP查询MySQL大量数据的时候内存占用分析
2011/07/22 PHP
php构造函数实例讲解
2013/11/13 PHP
如何让CI框架支持service层
2014/10/29 PHP
微信开发之php表单微信中自动提交两次问题解决办法
2017/01/08 PHP
PHP编程计算日期间隔天数的方法
2017/04/26 PHP
Laravel中Facade的加载过程与原理详解
2017/09/22 PHP
javascript 实用的文字链提示框效果
2010/06/30 Javascript
深入理解javascript原型链和继承
2014/09/23 Javascript
Javascript核心读书有感之类型、值和变量
2015/02/11 Javascript
arguments对象验证函数的参数是否合法
2015/06/26 Javascript
jquery原理以及学习技巧介绍
2015/11/11 Javascript
跟我学习javascript的执行上下文
2015/11/18 Javascript
详解Bootstrap创建表单的三种格式(一)
2016/01/04 Javascript
nodejs实例解析(输出hello world)
2017/01/03 NodeJs
JavaScript对象的浅拷贝与深拷贝实例分析
2018/07/25 Javascript
详解vue-cli项目开发/生产环境代理实现跨域请求
2019/07/23 Javascript
python使用mysqldb连接数据库操作方法示例详解
2013/12/03 Python
Python实现将一个正整数分解质因数的方法分析
2017/12/14 Python
基于Python对数据shape的常见操作详解
2018/12/25 Python
python协程之动态添加任务的方法
2019/02/19 Python
Dlib+OpenCV深度学习人脸识别的方法示例
2019/05/14 Python
关于Numpy数据类型对象(dtype)使用详解
2019/11/27 Python
python 串口读取+存储+输出处理实例
2019/12/26 Python
快速了解Python开发环境Spyder
2020/06/29 Python
详解如何用HTML5 Canvas API控制图片的缩放变换
2016/03/22 HTML / CSS
ProBikeKit德国:在线公路自行车专家
2018/06/03 全球购物
给学校的建议书
2014/03/12 职场文书
雷锋精神演讲稿
2014/05/13 职场文书
奥巴马获胜演讲稿
2014/05/15 职场文书
毕业生求职信
2014/06/10 职场文书
2014年学校党建工作总结
2014/11/11 职场文书
2015年度质量工作总结报告
2015/04/27 职场文书
selenium.webdriver中add_argument方法常用参数表
2021/04/08 Python