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 相关文章推荐
利用cookie记住背景颜色示例代码
Nov 04 Javascript
JavaScript实现动画打开半透明提示层的方法
Apr 21 Javascript
jquery中map函数遍历数组用法实例
May 18 Javascript
浅谈JavaScript变量的自动转换和语句
Jun 12 Javascript
Bootstrap栅格系统学习笔记
Nov 25 Javascript
js实现增加数字显示的环形进度条效果
Feb 05 Javascript
vue项目上传Github预览的实现示例
Nov 06 Javascript
JS中的算法与数据结构之常见排序(Sort)算法详解
Aug 16 Javascript
javascript实现弹幕墙效果
Nov 28 Javascript
Node.js+Vue脚手架环境搭建的方法步骤
Mar 08 Javascript
微信小程序个人中心的列表控件实现代码
Apr 26 Javascript
详解TS数字分隔符和更严格的类属性检查
May 06 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
深入Memcache的Session数据的多服务器共享详解
2013/06/13 PHP
Youku 视频绝对地址获取的方法详解
2013/06/26 PHP
php操作MongoDB类实例
2015/06/17 PHP
php+mysql+jquery实现简易的检索自动补全提示功能
2017/04/15 PHP
phpMyAdmin通过密码漏洞留后门文件
2018/11/20 PHP
PHP自动生成缩略图函数的源码示例
2019/03/18 PHP
JS中彻底删除JSON对象组成的数组中的元素
2020/09/22 PHP
javascript之bind使用介绍
2011/10/09 Javascript
node.js中的fs.fsync方法使用说明
2014/12/15 Javascript
AngularJS+Node.js实现在线聊天室
2015/08/28 Javascript
jQuery实现图片预加载效果
2015/11/27 Javascript
prototype框架中美元符号$用法分析
2016/01/22 Javascript
js实现的彩色方块飞舞奇幻效果
2016/01/27 Javascript
使用jQuery实现一个类似GridView的编辑,更新,取消和删除的功能
2017/03/15 Javascript
JavaScript实现简单的星星评分效果
2017/05/18 Javascript
SpringBoot+Vue前后端分离,使用SpringSecurity完美处理权限问题的解决方法
2018/01/09 Javascript
浅谈Webpack打包优化技巧
2018/06/12 Javascript
Makefile/cmake/node-gyp中区分判断不同平台的方法
2018/12/18 Javascript
vue 项目 iOS WKWebView 加载
2019/04/17 Javascript
JS原形与原型链深入详解
2020/05/09 Javascript
[36:54]Mineski vs Winstrike 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/17 DOTA
[42:32]完美世界DOTA2联赛PWL S2 LBZS vs FTD.C 第二场 11.27
2020/12/01 DOTA
python3+PyQt5实现自定义分数滑块部件
2018/04/24 Python
Python使用logging模块实现打印log到指定文件的方法
2018/09/05 Python
python判断一个数是否能被另一个整数整除的实例
2018/12/12 Python
关于python3中setup.py小概念解析
2019/08/22 Python
Spring实战之使用util:命名空间简化配置操作示例
2019/12/09 Python
CSS3教程(6):创建网站多列
2009/04/02 HTML / CSS
聪明的粉丝购买门票的地方:TickPick
2018/03/09 全球购物
李维斯法国官网:Levi’s法国
2019/07/13 全球购物
中学生励志演讲稿
2014/04/26 职场文书
优秀本科毕业生自荐信
2014/07/04 职场文书
民主生活会对照检查材料
2014/09/22 职场文书
人事行政助理岗位职责
2015/04/11 职场文书
Python如何使用logging为Flask增加logid
2021/03/30 Python
Python获取字典中某个key的value
2022/04/13 Python