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 相关文章推荐
使用prototype.js进行异步操作
Feb 07 Javascript
统一接口:为FireFox添加IE的方法和属性的js代码
Mar 25 Javascript
Jquery Ajax.ashx 高效分页实现代码
Oct 20 Javascript
实例说明为什么不要行内使用javascript
Apr 18 Javascript
js实现感应鼠标图片透明度变化的方法
Feb 20 Javascript
Bootstrap页面布局基础知识全面解析
Jun 13 Javascript
jquery 仿锚点跳转到页面指定位置的实例
Feb 14 Javascript
AngularJS之自定义服务详解(factory、service、provider)
Apr 14 Javascript
BootStrap selectpicker后台动态绑定数据
Jun 01 Javascript
AngularJS实现图片上传和预览功能的方法分析
Nov 08 Javascript
jfinal与bootstrap的登出实战详解
Nov 27 Javascript
JS实现表单中点击小眼睛显示隐藏密码框中的密码
Apr 13 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 discuz 主题表和回帖表的设计
2009/03/13 PHP
php is_file()和is_dir()用于遍历目录时用法注意事项
2010/03/02 PHP
div li的多行多列 无刷新分页示例代码
2013/10/16 PHP
php生成条形码的图片的实例详解
2017/09/13 PHP
php遍历目录下文件并按修改时间排序操作示例
2019/07/12 PHP
鼠标经过显示二级菜单js特效
2013/08/13 Javascript
AngularJS实现元素显示和隐藏的几个案例
2015/12/09 Javascript
基于javascript实现文字无缝滚动效果
2016/03/22 Javascript
jQuery的Each比JS原生for循环性能慢很多的原因
2016/07/05 Javascript
使用JQuery选择HTML遍历函数的方法
2016/09/17 Javascript
移动端使用localStorage缓存Js和css文的方法(web开发)
2016/09/20 Javascript
Node.js 使用命令行工具检查更新
2017/06/08 Javascript
jQuery EasyUI的TreeGrid查询功能实现方法
2017/08/08 jQuery
Three.js实现浏览器变动时进行自适应的方法
2017/09/26 Javascript
微信小程序分享功能之按钮button 边框隐藏和点击隐藏
2018/06/14 Javascript
详解jenkins自动化部署vue
2019/05/14 Javascript
Python、Javascript中的闭包比较
2015/02/04 Python
Python实现对比不同字体中的同一字符的显示效果
2015/04/23 Python
Python解析excel文件存入sqlite数据库的方法
2016/11/15 Python
Python3.6连接Oracle数据库的方法详解
2018/05/18 Python
Django 限制用户访问频率的中间件的实现
2018/08/23 Python
Python中IP地址处理IPy模块的方法
2019/08/16 Python
Python学习之路之pycharm的第一个项目搭建过程
2020/06/18 Python
python mongo 向数据中的数组类型新增数据操作
2020/12/05 Python
Python 利用flask搭建一个共享服务器的步骤
2020/12/05 Python
python BeautifulSoup库的安装与使用
2020/12/17 Python
HTML5学习笔记之html5与传统html区别
2016/01/06 HTML / CSS
美国经典刺绣和字母儿童服装特卖:Smocked Auctions
2018/07/16 全球购物
英文版区域经理求职信
2013/10/23 职场文书
美术社团活动总结
2014/06/27 职场文书
2014年员工工作总结范文
2014/11/18 职场文书
2014年反洗钱工作总结
2014/11/22 职场文书
2014年教师思想工作总结
2014/12/03 职场文书
感谢师恩主题班会
2015/08/17 职场文书
JavaScript 语句之常用 for 循环详解
2021/03/29 Javascript
Python循环之while无限迭代
2022/04/30 Python