JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【矩形情况】


Posted in Javascript onDecember 13, 2018

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

矩形包围盒,顾名思义,就是使用一个矩形来包围住图像,矩形的大小以刚好包围住图像为最佳,这种包围盒最适用的场景是刚好物体的形状接近于矩形。

在具体的应用中,描述矩形包围盒的的常用方式有以下两种,

一:采用最小最大顶点法描述AABB包围盒

JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【矩形情况】

上图中使用了最小最大顶点法来描述包围盒信息,由于是在屏幕坐标系中,y轴是向下延伸的,所以只需要保留矩形中坐标的最小值和最大值即可,即矩形的左上角和右下角的顶点,其他的点都在这两个点范围内。

在这种情况下要判断两个矩形是否碰撞只需要比较两个矩形顶点的坐标即可,假设矩形 A用(x1, y1)表示左上角,(x2, y2)表示右下角,矩形B用(x3, y3)表示左上角,(x4, y4)表示右下角,则满足下列条件则表示没有碰撞,反之则碰撞。

  • 没碰撞:x1>x4 或者x2<x3。
  • 没碰撞:y1>y4 或者y2<y3。

关键代码如下:

function hitTest(source, target) {
      /* 源物体和目标物体都包含 x, y 以及 width, height */
      return !(
        ( ( source.y + source.r ) < ( target.y ) ) ||
        ( source.y > ( target.y + target.r ) ) ||
        ( ( source.x + source.r ) < target.x ) ||
        ( source.x > ( target.x + target.r ) )
      );
    }

DEMO代码:

<!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="hitTest">否</span></h1>
<canvas id="stage"></canvas>
</body>
<script>
  window.onload = function () {
    var stage = document.querySelector('#stage'),
      ctx = stage.getContext('2d');
    stage.width = 400;
    stage.height = 400;
    //栅格线条
    function drawGrid(context, color, stepx, stepy) {
      context.strokeStyle = color;
      context.lineWidth = 0.5;
      for (var i = stepx + 0.5; i < context.canvas.width; i += stepx) {
        context.beginPath();
        context.moveTo(i, 0);
        context.lineTo(i, context.canvas.height);
        context.stroke();
      }
      for (var i = stepy + 0.5; i < context.canvas.height; i += stepy) {
        context.beginPath();
        context.moveTo(0, i);
        context.lineTo(context.canvas.width, i);
        context.stroke();
      }
    }
    var rect = {
      x: stage.width / 2 - 20,
      y: stage.height / 2 - 20,
      r: 40,
      c: "red"
    }, rects = [];;
    rects.push(rect);
    for (var i = 0; i < 10; i++) {
      var trace = {
        x: 40 * i,
        y: 40 * i,
        r: 40,
        c: "blue"
      };
      rects.push(trace);
    }
    function createRect(x, y, r, c) {
      ctx.beginPath();
      ctx.fillStyle = c;
      ctx.rect(x, y, r, r);
      ctx.fill();
    }
    document.onkeydown = function (event) {
      var e = event || window.event || arguments.callee.caller.arguments[0];
      //根据地图数组碰撞将测
      switch (e.keyCode) {
        case 37:
          console.log("Left");
          if (rects[0].x > 0) {
            rects[0].x -= 2;
          }
          break;
        case 38:
          console.log("Top");
          if (rects[0].y > 0) {
            rects[0].y -= 2;
          }
          break;
        case 39:
          console.log("Right");
          if (rects[0].x < stage.width) {
            rects[0].x += 2;
          }
          break;
        case 40:
          console.log("Bottom");
          if (rects[0].y < stage.height) {
            rects[0].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;
      rects[0].x = x - rects[0].r/2;
      rects[0].y = y - rects[0].r/2;
    });
    function hitTest(source, target) {
      /* 源物体和目标物体都包含 x, y 以及 width, height */
      return !(
        ( ( source.y + source.r ) < ( target.y ) ) ||
        ( source.y > ( target.y + target.r ) ) ||
        ( ( source.x + source.r ) < target.x ) ||
        ( source.x > ( target.x + target.r ) )
      );
    }
    function update() {
      ctx.globalAlpha = 1;
      ctx.clearRect(0, 0, 400, 400);
      drawGrid(ctx, 'lightgray', 40, 40);
      document.querySelector('.hitTest').innerHTML = "否";
      for (var i = 1, len = rects.length; i < len; i++) {
        createRect(rects[i].x, rects[i].y, rects[i].r, rects[i].c);
        var flag = hitTest(rects[0], rects[i]);
        if (flag) {
          document.querySelector('.hitTest').innerHTML = "是";
          ctx.globalAlpha = 0.5;
        }
      }
      createRect(rects[0].x, rects[0].y, rects[0].r, rects[0].c);
      requestAnimationFrame(update);
    }
    update();
  };
</script>
</html>

这里使用在线HTML/CSS/JavaScript代码运行工具:http://tools.3water.com/code/HtmlJsRun 测试上述代码运行效果如下:

JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【矩形情况】

二:采用点和半径描述AABB包围盒

JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【矩形情况】

在上图中使用了中心点和对应两个轴的半径来描述包围盒信息,假设有两个矩形A和B,矩形A 的中心坐标为A(x1, y1),宽度和高度分别为rx1、ry1,矩形B 的中心坐标为B(x2, y2),宽度和高度分别为rx1、ry1,矩形B 的中心坐标为B(x2, y2),宽度和高度分别是rx2、ry2,则采用这种包围盒检测方式如下。

如果满足两个矩形在x方向的距离小于两个矩形宽度和的一半,并且在y方向上的距离小于两个矩形高度和的一半则表示两个矩形有重叠,即表示发生碰撞,换成公式如下:

X方向满足:|x2-x1|<=rx1+rx2并且Y方向满足:|y2-y1|<=ry1+ry2

当然,也可以把这种形式换算成第一种形式演算,这两种方式很显然第一种的效率比较高效一点,毕竟第二种算法需要使用

Math.abs获取绝对值,第一种只是单纯使用了坐标比较。

以上所描述的矩形包围盒也称为 AABB(轴对齐)包围盒,轴对齐包围盒中的矩形的四条边分别和坐标轴平行,实际上也就是表示该矩形没有进行过旋转操作,使用轴对齐包围盒检测算法比较简单高效,精度上也能满足大多数条件,因此实际应用中也比较多。

有兴趣的可以搜索下OBB(定向接线)包围盒。

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

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

Javascript 相关文章推荐
9个javascript语法高亮插件 推荐
Jul 18 Javascript
JavaScript判断DOM何时加载完毕的技巧
Nov 11 Javascript
jQuery动画效果-fadeIn fadeOut淡入浅出示例代码
Aug 28 Javascript
Js使用WScript.Shell对象执行.bat文件和cmd命令
Dec 18 Javascript
JavaScript实现自动弹出窗口并自动关闭窗口的方法
Aug 06 Javascript
jQuery each函数源码分析
May 25 Javascript
Angular directive递归实现目录树结构代码实例
May 05 Javascript
Jquery中.bind()、.live()、.delegate()和.on()之间的区别详解
Aug 01 jQuery
薪资那么高的Web前端必看书单
Oct 13 Javascript
JS使用tofixed与round处理数据四舍五入的区别
Oct 25 Javascript
Angular6封装http请求的步骤详解
Aug 13 Javascript
12个提高JavaScript技能的概念(小结)
May 09 Javascript
详解Express笔记之动态渲染HTML(新手入坑)
Dec 13 #Javascript
js实现黑白div块画空心的图形
Dec 13 #Javascript
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【圆形情况】
Dec 13 #Javascript
示例vue 的keep-alive缓存功能的实现
Dec 13 #Javascript
Element UI框架中巧用树选择器的实现
Dec 12 #Javascript
vue-cli中安装方法(图文详细步骤)
Dec 12 #Javascript
新版小程序登录授权的方法
Dec 12 #Javascript
You might like
PHP 函数学习简单小结
2010/07/08 PHP
php数据库配置文件一般做法分享
2012/07/07 PHP
php文件夹与文件目录操作函数介绍
2013/09/09 PHP
PHP+shell实现多线程的方法
2015/07/01 PHP
phalcon model在插入或更新时会自动验证非空字段的解决办法
2016/12/29 PHP
PHP常量DIRECTORY_SEPARATOR原理及用法解析
2020/11/10 PHP
js更优雅的兼容
2010/08/12 Javascript
jQuery1.3.2 升级到jQuery1.4.4需要修改的地方
2011/01/06 Javascript
Jquery插件之打造自定义的select标签
2011/11/30 Javascript
JavaScript高级程序设计阅读笔记(五) ECMAScript中的运算符(一)
2012/02/27 Javascript
jquery 无限级联菜单案例分享
2013/03/26 Javascript
JavaScript中String.prototype用法实例
2015/05/20 Javascript
JS实现的网页背景闪电闪烁效果代码
2015/10/17 Javascript
高效利用Angular中内置服务$http、$location等
2016/03/22 Javascript
详解js的六大数据类型
2016/12/27 Javascript
利用jQuery实现一个简单的表格上下翻页效果
2017/03/14 Javascript
10行原生JS实现文字无缝滚动(超简单)
2018/01/02 Javascript
Node Puppeteer图像识别实现百度指数爬虫的示例
2018/02/22 Javascript
Angular5中调用第三方js插件的方法
2018/02/26 Javascript
Vue仿微信app页面跳转动画效果
2019/08/21 Javascript
Node绑定全局TraceID的实现方法
2019/11/14 Javascript
解决三元运算符 报错“SyntaxError: can''t assign to conditional expression”
2020/02/12 Javascript
[01:03:56]Mineski vs TNC 2018国际邀请赛淘汰赛BO1 8.21
2018/08/22 DOTA
python判断字符串是否包含子字符串的方法
2015/03/24 Python
Python实现爬虫设置代理IP和伪装成浏览器的方法分享
2018/05/07 Python
Python 实现Windows开机运行某软件的方法
2018/10/14 Python
python实现从文件中读取数据并绘制成 x y 轴图形的方法
2018/10/14 Python
对matplotlib改变colorbar位置和方向的方法详解
2018/12/13 Python
python爬虫实现中英翻译词典
2019/06/25 Python
Python+selenium点击网页上指定坐标的实例
2019/07/05 Python
Python3 mmap内存映射文件示例解析
2020/03/23 Python
ffmpeg+Python实现B站MP4格式音频与视频的合并示例代码
2020/10/21 Python
CSS3 特效范例整理
2011/08/22 HTML / CSS
超市端午节活动方案
2014/01/23 职场文书
部门优秀员工推荐信
2015/03/24 职场文书
2015年度培训工作总结范文
2015/04/02 职场文书