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库 开发规则
Jan 31 Javascript
Javascript 浮点运算的问题分析与解决方法
Aug 27 Javascript
JS控件ASP.NET的treeview控件全选或者取消(示例代码)
Dec 16 Javascript
JavaScript制作windows经典扫雷小游戏
Mar 31 Javascript
jQuery插件编写步骤详解
Jun 03 Javascript
详解Angualr 组件间通信
Jan 21 Javascript
jquery实现手机端单店铺购物车结算删除功能
Feb 22 Javascript
详解angular2实现ng2-router 路由和嵌套路由
Mar 24 Javascript
Vue和Bootstrap的整合思路详解
Jun 30 Javascript
微信小程序实现自定义picker选择器弹窗内容
May 26 Javascript
vue-cli4.5.x快速搭建项目
May 30 Vue.js
四十九个javascript小知识实用技巧
Nov 20 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基于接口技术实现简单的多态应用完整实例
2017/04/26 PHP
JS对外部文件的加载及对IFRMAME的加载的实现,当加载完成后,指定指向方法(方法回调)
2011/07/04 Javascript
点击标签切换和自动切换DIV选项卡
2014/08/10 Javascript
AngularJS HTML编译器介绍
2014/12/06 Javascript
JavaScript中的数组操作介绍
2014/12/30 Javascript
JavaScript中Cookie操作实例
2015/01/09 Javascript
Javascript基础教程之for循环
2015/01/18 Javascript
JavaScript使用yield模拟多线程的方法
2015/03/19 Javascript
老生常谈js动态添加事件--- 事件委托
2016/07/19 Javascript
javascript字符串对象常用api函数小结(连接,替换,分割,转换等)
2016/09/20 Javascript
jQuery实现点击后高亮背景固定显示的菜单效果【附demo源码下载】
2016/09/21 Javascript
关于Jquery中的事件绑定总结
2016/10/26 Javascript
Angular使用$http.jsonp发送跨站请求的方法
2017/03/16 Javascript
php 修改密码实现代码
2017/05/24 Javascript
ReactNative短信验证码倒计时控件的实现代码
2017/07/20 Javascript
layui select动态添加option的实例
2018/03/07 Javascript
浅谈Vue 数据响应式原理
2018/05/07 Javascript
python网络编程学习笔记(七):HTML和XHTML解析(HTMLParser、BeautifulSoup)
2014/06/09 Python
基于python3实现socket文件传输和校验
2018/07/28 Python
使用Django2快速开发Web项目的详细步骤
2019/01/06 Python
django之对FileField字段的upload_to的设定方法
2019/07/28 Python
numpy ndarray 取出满足特定条件的某些行实例
2019/12/05 Python
Python 随机按键模拟2小时
2020/12/30 Python
css3实现画半圆弧线的示例代码
2017/11/06 HTML / CSS
美国帽子俱乐部商店:Hat Club
2019/07/05 全球购物
马歇尔耳机官网:Marshall Headphones
2020/02/04 全球购物
如果NULL定义成#define NULL((char *)0)难道不就可以向函数传入不加转换的NULL了吗
2012/02/15 面试题
高中数学教师求职信
2013/10/30 职场文书
残疾人小组计划书
2014/04/27 职场文书
2014法院干警廉洁警示教育思想汇报
2014/09/13 职场文书
教师思想作风整顿个人剖析材料
2014/10/10 职场文书
党的群众路线教育实践活动个人整改方案
2014/10/25 职场文书
Python实战之疫苗研发情况可视化
2021/05/18 Python
Python爬虫入门案例之爬取去哪儿旅游景点攻略以及可视化分析
2021/10/16 Python
Go语言读取txt文档的操作方法
2022/01/22 Golang
Go本地测试解耦任务拆解及沟通详解Go本地测试的思路沟通的重要性总结
2022/06/21 Golang