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 相关文章推荐
JavaScript 判断日期格式是否正确的实现代码
Jul 04 Javascript
EXTJS记事本 当CompositeField遇上RowEditor
Jul 31 Javascript
Javascript的时间戳和php的时间戳转换注意事项
Apr 12 Javascript
jQuery实现感应鼠标动画效果自动伸长的输入框实例
Feb 24 Javascript
分享10个原生JavaScript技巧
Apr 20 Javascript
不得不分享的JavaScript常用方法函数集(下)
Dec 25 Javascript
基于JS判断iframe是否加载成功的方法(多种浏览器)
May 13 Javascript
Node.js+Express配置入门教程
May 19 Javascript
jQuery+PHP实现微信转盘抽奖功能的方法
May 25 Javascript
原JS实现banner图的常用功能
Jun 12 Javascript
使用vue2实现带地区编号和名称的省市县三级联动效果
Nov 05 Javascript
vue 自定义组件的写法与用法详解
Mar 04 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的socket通信
2015/08/11 PHP
PHP框架Laravel插件Pagination实现自定义分页
2020/04/22 PHP
Thinkphp微信公众号支付接口
2016/08/04 PHP
docker-compose部署php项目实例详解
2019/07/30 PHP
CL vs ForZe BO5 第五场 2.13
2021/03/10 DOTA
网页自动刷新,不产生嗒嗒声的一个解决方法
2007/03/27 Javascript
html数组字符串拼接的最快方法
2009/09/16 Javascript
jQuery库与其他JS库冲突的解决办法
2010/02/07 Javascript
jquery ui dialog ie8出现滚动条的解决方法
2010/12/06 Javascript
php is_numberic函数造成的SQL注入漏洞
2014/03/10 Javascript
js数值计算时使用parseInt进行数据类型转换(jquery)
2014/10/07 Javascript
推荐10 款 SVG 动画的 JavaScript 库
2015/03/24 Javascript
jfinal与bootstrap的登录跳转实战演习
2015/09/22 Javascript
jQuery中的siblings用法实例分析
2015/12/24 Javascript
深入分析Javascript事件代理
2016/01/30 Javascript
Angular.js实现动态加载组件详解
2017/05/28 Javascript
vue动画效果实现方法示例
2019/03/18 Javascript
在layui中对table中的数据进行判断(0、1)转换为提示信息的方法
2019/09/28 Javascript
JavaScript Blob对象原理及用法详解
2020/10/14 Javascript
[01:24]2014DOTA2 TI第二日 YYF表示这届谁赢都有可能
2014/07/11 DOTA
[46:44]DOTA2-DPC中国联赛 正赛 Ehome vs PSG.LGD BO3 第二场 3月7日
2021/03/11 DOTA
详解Python实现多进程异步事件驱动引擎
2017/08/25 Python
如何高效使用Python字典的方法详解
2017/08/31 Python
python实现银行实战系统
2020/02/26 Python
Python多线程操作之互斥锁、递归锁、信号量、事件实例详解
2020/03/24 Python
Python基于httpx模块实现发送请求
2020/07/07 Python
把Anaconda中的环境导入到Pycharm里面的方法步骤
2020/10/30 Python
解决pytorch下出现multi-target not supported at的一种可能原因
2021/02/06 Python
美国南部最大的家族百货公司:Belk
2017/01/30 全球购物
中班开学寄语
2014/04/04 职场文书
取保候审保证书
2014/04/30 职场文书
中班教师个人总结
2015/02/05 职场文书
煤矿百日安全活动总结
2015/05/07 职场文书
2016年社区综治宣传月活动总结
2016/03/16 职场文书
授权协议书范本(3篇)
2019/10/15 职场文书
MySQL8.0 Undo Tablespace管理详解
2022/06/16 MySQL