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 相关文章推荐
运用Windows XP附带的Msicuu.exe、Msizap.exe来彻底卸载顽固程序
Apr 21 Javascript
javascript 写类方式之七
Jul 05 Javascript
获取元素距离浏览器周边的位置的方法getBoundingClientRect
Apr 17 Javascript
开发中可能会用到的jQuery小技巧
Mar 07 Javascript
鼠标移到图片上变大显示而不是放大镜效果
Jun 15 Javascript
小心!AngularJS结合RequireJS做文件合并压缩的那些坑
Jan 09 Javascript
jQuery插件HighCharts实现的2D回归直线散点效果示例【附demo源码下载】
Mar 09 Javascript
jQueryeasyui 中如何使用datetimebox 取两个日期间相隔的天数
Jun 13 jQuery
基于ajax和jsonp的原生封装(实例)
Oct 16 Javascript
浅谈angular4.0中路由传递参数、获取参数最nice的写法
Mar 12 Javascript
微信上传视频文件提示(推荐)
Nov 22 Javascript
详解vue中v-for的key唯一性
May 15 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
php基础知识:函数基础知识
2006/12/13 PHP
php 远程图片保存到本地的函数类
2008/12/08 PHP
无法在发生错误时创建会话,请检查 PHP 或网站服务器日志,并正确配置 PHP 安装(win+linux)
2012/05/05 PHP
使用PHP下载CSS文件中的图片的代码
2013/09/24 PHP
用正则xmlHttp实现的偷(转)
2007/01/22 Javascript
javascript 面向对象思想 附源码
2009/07/07 Javascript
ASP.NET jQuery 实例10 动态修改hyperlink的URL值
2012/02/03 Javascript
Javascript 判断是否存在函数的方法
2013/01/03 Javascript
Javascript事件实例详解
2013/11/06 Javascript
JavaScript原型链示例分享
2014/01/26 Javascript
JavaScript定时器和优化的取消定时器方法
2015/07/03 Javascript
全面解析Bootstrap中nav、collapse的使用方法
2016/05/22 Javascript
用自定义图片代替原生checkbox实现全选,删除以及提交的方法
2016/10/18 Javascript
JSON字符串和JSON对象相互转化实例详解
2017/01/05 Javascript
JS库 Highlightjs 添加代码行号的实现代码
2017/09/13 Javascript
react.js组件实现拖拽复制和可排序的示例代码
2018/08/20 Javascript
vue+高德地图写地图选址组件的方法
2019/05/18 Javascript
原生js实现each方法实例代码详解
2019/05/27 Javascript
[01:03:41]DOTA2-DPC中国联赛 正赛 Dynasty vs XG BO3 第三场 2月2日
2021/03/11 DOTA
Python实现的简单dns查询功能示例
2017/05/24 Python
Python重新加载模块的实现方法
2018/10/16 Python
Python爬取12306车次信息代码详解
2020/08/12 Python
python3将变量输入的简单实例
2020/08/19 Python
CSS3实现大小不一的粒子旋转加载动画
2016/04/21 HTML / CSS
联想美国官方商城:Lenovo美国
2017/06/19 全球购物
PHP如何去执行一个SQL语句
2016/03/05 面试题
怎样建立和理解非常复杂的声明?例如定义一个包含N 个指向返回 指向字符的指针的函数的指针的数组?
2013/03/19 面试题
办公室保洁员岗位职责
2013/12/02 职场文书
党校培训思想汇报
2013/12/30 职场文书
党委书记个人对照检查材料
2014/09/15 职场文书
王兆力在市委党的群众路线教育实践活动总结大会上的讲话稿
2014/10/25 职场文书
2014年体育教师工作总结
2014/12/03 职场文书
2014社区健康教育工作总结
2014/12/16 职场文书
怎样写家长意见
2015/06/04 职场文书
CentOS7和8下安装Maven3.8.4
2022/04/07 Servers
VUE解决跨域问题Access to XMLHttpRequest at
2022/05/06 Vue.js